diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000000..567b0e69911 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,118 @@ +# Contributing + +This document describes how you can contribute to Telegram-iOS. Please read it carefully. + +**Table of Contents** + +* [What contributions are accepted](#what-contributions-are-accepted) +* [Build instructions](#build-instructions) +* [Pull upstream changes into your fork regularly](#pull-upstream-changes-into-your-fork-regularly) +* [How to get your pull request accepted](#how-to-get-your-pull-request-accepted) + * [Keep your pull requests limited to a single issue](#keep-your-pull-requests-limited-to-a-single-issue) + * [Squash your commits to a single commit](#squash-your-commits-to-a-single-commit) + * [Don't mix code changes with whitespace cleanup](#dont-mix-code-changes-with-whitespace-cleanup) + * [Keep your code simple!](#keep-your-code-simple) + * [Test your changes!](#test-your-changes) + * [Write a good commit message](#write-a-good-commit-message) + +## What contributions are accepted + +We highly appreciate your contributions in the matter of fixing bugs and optimizing the Telegram-iOS source code and its documentation. In case of fixing the existing user experience please push to your fork and [submit a pull request][pr]. + +Wait for us. We try to review your pull requests as fast as possible. +If we find issues with your pull request, we may suggest some changes and improvements. + +Unfortunately we **do not merge** any pull requests that have new feature implementations, translations to new languages and those which introduce any new user interface elements. + +If you have a translations-related contribution, check out [Translations platform][translate]. + +Telegram-iOS is not a standalone application but a part of [Telegram project][telegram], so all the decisions about the features, languages, user experience, user interface and the design are made inside Telegram team, often according to some roadmap which is not public. + +## Build instructions + +See the [README.md][build_instructions] for details on the various build +environments. + +## Pull upstream changes into your fork regularly + +Telegram-iOS is advancing quickly. It is therefore critical that you pull upstream changes into your fork on a regular basis. Nothing is worse than putting in a days of hard work into a pull request only to have it rejected because it has diverged too far from upstream. + +To pull in upstream changes: + + git remote add upstream https://github.com/TelegramMessenger/Telegram-iOS.git + git fetch upstream master + +Check the log to be sure that you actually want the changes, before merging: + + git log upstream/master + +Then rebase your changes on the latest commits in the `master` branch: + + git rebase upstream/master + +After that, you have to force push your commits: + + git push --force + +For more info, see [GitHub Help][help_fork_repo]. + +## How to get your pull request accepted + +We want to improve Telegram-iOS with your contributions. But we also want to provide a stable experience for our users and the community. Follow these rules and you should succeed without a problem! + +### Keep your pull requests limited to a single issue + +Pull requests should be as small/atomic as possible. Large, wide-sweeping changes in a pull request will be **rejected**, with comments to isolate the specific code in your pull request. Some examples: + +* If you are making spelling corrections in the docs, don't modify other files. +* If you are adding new functions don't '*cleanup*' unrelated functions. That cleanup belongs in another pull request. + +#### Squash your commits to a single commit + +To keep the history of the project clean, you should make one commit per pull request. +If you already have multiple commits, you can add the commits together (squash them) with the following commands in Git Bash: + +1. Open `Git Bash` (or `Git Shell`) +2. Enter following command to squash the recent {N} commits: `git reset --soft HEAD~{N} && git commit` (replace `{N}` with the number of commits you want to squash) +3. Press i to get into Insert-mode +4. Enter the commit message of the new commit +5. After adding the message, press ESC to get out of the Insert-mode +6. Write `:wq` and press Enter to save the new message or write `:q!` to discard your changes +7. Enter `git push --force` to push the new commit to the remote repository + +For example, if you want to squash the last 5 commits, use `git reset --soft HEAD~5 && git commit` + +### Don't mix code changes with whitespace cleanup + +If you change two lines of code and correct 200 lines of whitespace issues in a file the diff on that pull request is functionally unreadable and will be **rejected**. Whitespace cleanups need to be in their own pull request. + +### Keep your code simple! + +Please keep your code as clean and straightforward as possible. +Furthermore, the pixel shortage is over. We want to see: + +* `opacity` instead of `o` +* `placeholder` instead of `ph` +* `myFunctionThatDoesThings()` instead of `mftdt()` + +### Test your changes! + +Before you submit a pull request, please test your changes. Verify that Telegram-iOS still works and your changes don't cause other issue or crashes. + +### Write a good commit message + +* Explain why you make the changes. [More infos about a good commit message.][commit_message] + +* If you fix an issue with your commit, please close the issue by [adding one of the keywords and the issue number][closing-issues-via-commit-messages] to your commit message. + + For example: `Fix #545` + +[//]: # (LINKS) +[telegram]: https://telegram.org/ +[help_fork_repo]: https://help.github.com/articles/fork-a-repo/ +[help_change_commit_message]: https://help.github.com/articles/changing-a-commit-message/ +[commit_message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html +[pr]: https://github.com/TelegramMessenger/Telegram-iOS/compare +[build_instructions]: https://github.com/TelegramMessenger/Telegram-iOS#quick-compilation-guide +[closing-issues-via-commit-messages]: https://help.github.com/articles/closing-issues-via-commit-messages/ +[translate]: https://translations.telegram.org diff --git a/Nicegram/NGUI/Sources/NicegramSettingsController.swift b/Nicegram/NGUI/Sources/NicegramSettingsController.swift index dff2a144593..3727cdcbaf8 100644 --- a/Nicegram/NGUI/Sources/NicegramSettingsController.swift +++ b/Nicegram/NGUI/Sources/NicegramSettingsController.swift @@ -31,6 +31,7 @@ import NGWebUtils import NGAiChatUI import NGCardUI import NGAppCache +import NGCore import var NGCoreUI.strings import NGDoubleBottom import NGQuickReplies @@ -543,7 +544,7 @@ private enum NicegramSettingsControllerEntry: ItemListNodeEntry { return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: section) case let .unblock(text, url): return ItemListActionItem(presentationData: presentationData, title: text, kind: .neutral, alignment: .natural, sectionId: section, style: .blocks) { - UIApplication.shared.openURL(url) + CoreContainer.shared.urlOpener().open(url) } case let .Account(text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: section) diff --git a/Package.resolved b/Package.resolved index f53c29132a7..efa38352272 100644 --- a/Package.resolved +++ b/Package.resolved @@ -42,7 +42,7 @@ "location" : "git@bitbucket.org:mobyrix/nicegram-assistant-ios.git", "state" : { "branch" : "develop", - "revision" : "c3686ba5c4f4ad5eb0273bc9dfd5784e0971a5a2" + "revision" : "35b2c2c88c8d2cb3d25cfa95e15e588f90578994" } }, { @@ -59,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/SDWebImage/SDWebImage.git", "state" : { - "revision" : "1b9a2e902cbde5fdf362faa0f4fd76ea74d74305", - "version" : "5.18.5" + "revision" : "0383fd49fe4d9ae43f150f24693550ebe6ef0d14", + "version" : "5.18.6" } }, { @@ -86,8 +86,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-argument-parser", "state" : { - "revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531", - "version" : "1.2.3" + "revision" : "c8ed701b513cf5177118a175d85fbbbcd707ab41", + "version" : "1.3.0" } }, { diff --git a/Telegram/BUILD b/Telegram/BUILD index d3d1bc57869..e5ab67e13f3 100644 --- a/Telegram/BUILD +++ b/Telegram/BUILD @@ -396,6 +396,7 @@ swift_library( "//submodules/DrawingUI:DrawingUIResources", "//submodules/TelegramUI:TelegramUIResources", "//submodules/TelegramUI:TelegramUIAssets", + "//submodules/TelegramUI/Components/Calls/CallScreen:Assets", ":GeneratedPresentationStrings/Resources/PresentationStrings.data", ":GoogleService-Info", ] + NGRESOURCES, @@ -2079,12 +2080,11 @@ xcodeproj( "Debug": { "//command_line_option:compilation_mode": "dbg", }, - #"Release": { - # "//command_line_option:compilation_mode": "opt", - #}, + "Release": { + "//command_line_option:compilation_mode": "opt", + }, }, default_xcode_configuration = "Debug" - ) # Temporary targets used to simplify webrtc build tests diff --git a/Telegram/Telegram-iOS/Resources/Transcribe.tgs b/Telegram/Telegram-iOS/Resources/Transcribe.tgs new file mode 100644 index 00000000000..341e57cd00e Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Transcribe.tgs differ diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 0a88c7a78db..02a04605f80 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -10464,3 +10464,135 @@ Sorry for the inconvenience."; "BoostGift.StartConfirmation.Start" = "Start"; "Channel.Info.Stats" = "Statistics and Boosts"; + +"BoostGift.StartConfirmation.Title" = "Start Giveaway"; +"BoostGift.StartConfirmation.Text" = "Are you sure you want to start giveaway now?"; +"BoostGift.StartConfirmation.Start" = "Start"; + +"Channel.Info.Stats" = "Statistics and Boosts"; + +"Conversation.FreeTranscriptionLimitTooltip_1" = "You have **%@** free voice transcription left this week."; +"Conversation.FreeTranscriptionLimitTooltip_any" = "You have **%@** free voice transcriptions left this week."; + +"Conversation.FreeTranscriptionCooldownTooltip_1" = "You have used all your **%@** free transcription this week."; +"Conversation.FreeTranscriptionCooldownTooltip_any" = "You have used all your **%@** free transcriptions this week."; +"Conversation.FreeTranscriptionWaitOrSubscribe" = "Wait until **%@** to use it again or subscribe to [Telegram Premium]() now."; + +"Notification.GiveawayResults_1" = "**%@** winner of the giveaway was randomly selected by Telegram and received their gift link in a private message."; +"Notification.GiveawayResults_any" = "**%@** winners of the giveaway were randomly selected by Telegram and received their gift links in private messages."; + +"Notification.GiveawayResultsNoWinners_1" = "Due to the giveaway terms, no winners could be selected by Telegram, a gift link was forwarded to channel administrators."; +"Notification.GiveawayResultsNoWinners_any" = "Due to the giveaway terms, no winners could be selected by Telegram, all **%@** gift links were forwarded to channel administrators."; + +"Notification.GiveawayResultsMixedWinners_1" = "**%@** winner of the giveaway was randomly selected by Telegram and received their gift link in a private message."; +"Notification.GiveawayResultsMixedWinners_any" = "**%@** winners of the giveaway were randomly selected by Telegram and received their gift links in private messages."; +"Notification.GiveawayResultsMixedUnclaimed_1" = "**%@** undistributed gift link was forwarded to channel administrators"; +"Notification.GiveawayResultsMixedUnclaimed_any" = "**%@** undistributed gift links were forwarded to channel administrators"; + +"Chat.Giveaway.DeleteConfirmation.Title" = "Do you want to delete the Giveaway Announcement?"; +"Chat.Giveaway.DeleteConfirmation.Text" = "Deleting this message won't cancel the giveaway - the winners will still be selected on **%@**.\n\nOnce deleted, the Giveaway Announcement cannot be recovered."; + +"Chat.SimilarChannels" = "Similar Channels"; +"Chat.SimilarChannels.Join" = "Join"; +"Chat.SimilarChannels.JoinedChannel" = "You joined channel **%@**."; +"Chat.SimilarChannels.MoreChannels" = "More Channels"; + +"Wallpaper.ApplyForMe" = "Apply for Me"; +"Wallpaper.ApplyForBoth" = "Apply for Me and %@"; + +"Premium.VoiceToText.Proceed" = "About Telegram Premium"; +"Premium.Wallpaper.Proceed" = "About Telegram Premium"; +"Premium.Colors.Proceed" = "About Telegram Premium"; + +"Notification.YouChangedWallpaperBoth" = "You set a new wallpaper for %@ and you."; + +"Stats.ReactionsByEmotionTitle" = "REACTIONS BY EMOTION"; +"Stats.StoryInteractionsTitle" = "STORIES INTERACTIONS"; +"Stats.StoryReactionsByEmotionTitle" = "STORIES REACTIONS BY EMOTION"; + +"Stats.MessageReactionsTitle" = "REACTIONS"; + +"MediaEditor.RemoveVideo" = "Remove Video"; + +"Conversation.LaunchApp" = "LAUNCH APP"; + +"Message.AdSponsoredLabel" = "Sponsored"; +"Message.AdRecommendedLabel" = "Recommended"; + +"Stats.StoryTitle" = "Story Statistics"; + +"Channel.Info.Settings" = "Channel Settings"; + +"Story.Context.ViewStats" = "View Statistics"; + +"Share.RepostStory" = "Repost\nStory"; + +"PeerInfo.PaneRecommended" = "Similar Channels"; + +"Story.ViewList.ContextSortReposts" = "Reposts First"; + +"ShortTime.JustNow" = "now"; +"ShortTime.MinutesAgo_1" = "%@m"; +"ShortTime.MinutesAgo_any" = "%@m"; +"ShortTime.HoursAgo_1" = "%@h"; +"ShortTime.HoursAgo_any" = "%@h"; +"ShortTime.AtDate" = "%@"; +"ShortTime.AtPreciseDate" = "%@ at %@"; + +"Stats.Message.Reactions" = "Reactions"; +"Stats.ReactionsPerPost" = "Reactions Per Post"; +"Stats.ViewsPerStory" = "Views Per Story"; +"Stats.SharesPerStory" = "Shares Per Story"; +"Stats.ReactionsPerStory" = "Reactions Per Story"; + +"Channel.SimilarChannels.ShowMore" = "Show More Channels"; +"Channel.SimilarChannels.ShowMoreInfo" = "Subscribe to [Telegram Premium]()\nto unlock up to **100** similar channels."; + +"Premium.Colors" = "Name and Profile Colors"; +"Premium.ColorsInfo" = "Choose a color and logo for your profile and replies to your messages."; + +"Premium.Wallpapers" = "Wallpapers for Both Sides"; +"Premium.WallpapersInfo" = "Set custom wallpapers for you and your chat partner."; + +"MediaEditor.VideoRemovalConfirmation" = "Are you sure you want to delete video message?"; +"MediaEditor.HoldToRecordVideo" = "Hold to record video"; + +"Chat.ChannelRecommendation.PremiumTooltip" = "Subcribe to [Telegram Premium]() to unlock up to **100** channels."; + +"Story.ForwardAuthorHiddenTooltip" = "The account was hidden by the user"; + +"Premium.Limits.RecommendedChannels" = "Similar Channels"; +"Premium.Limits.RecommendedChannelsInfo" = "View up to 100 similar channels."; + +"ChannelBoost.CustomReactions" = "Custom Reactions"; +"ChannelBoost.CustomReactionsText" = "Your channel needs to reach **Level %1$@** to add **%2$@** custom emoji as reactions.\n\nAsk your **Premium** subscribers to boost your channel with this link:"; + +"Settings.YourColor" = "Your Color"; + +"ChannelReactions.Reactions" = "Reactions"; +"ChannelReactions.UnsavedChangesAlertTitle" = "Unsaved Changes"; +"ChannelReactions.UnsavedChangesAlertText" = "You have changed the list of reactions. Apply changes?"; +"ChannelReactions.UnsavedChangesAlertDiscard" = "Discard"; +"ChannelReactions.UnsavedChangesAlertApply" = "Apply"; +"ChannelReactions.ToastMaxReactionsReached" = "You can select at most 100 reactions."; +"ChannelReactions.ToastLevelBoostRequired" = "Your channel needs to reach **Level %1$@** to add **%2$@** custom emoji as reactions.**"; +"ChannelReactions.GeneralInfoLabel" = "You can add emoji from any emoji pack as a reaction."; +"ChannelReactions.ReactionsSectionTitle" = "AVAILABLE REACTIONS"; +"ChannelReactions.ReactionsInfoLabel" = "You can also [create your own]() emoji packs and use them."; +"ChannelReactions.SaveAction" = "Update Reactions"; +"ChannelReactions.LevelRequiredLabel" = "Level %1$@ Required"; + +"ProfileColorSetup.ResetAction" = "Reset Profile Color"; +"ProfileColorSetup.IconSectionTitle" = "ADD ICON TO PROFILE"; +"ProfileColorSetup.AccountColorInfoLabel" = "Choose a color for your profile"; +"ProfileColorSetup.ChannelColorInfoLabel" = "Choose a color for channel's profile"; +"ProfileColorSetup.TitleName" = "Name"; +"ProfileColorSetup.TitleProfile" = "Profile"; +"ProfileColorSetup.TitleChannelColor" = "Set Channel Color"; +"ProfileColorSetup.ToastAccountColorUpdated" = "Your color has been updated."; + +"Chat.TopicIsClosedLabel" = "Topic \"%1$@\" is closed"; +"Chat.InputPlaceholderReplyInTopic" = "Reply in %1$@"; +"Chat.InputPlaceholderMessageInTopic" = "Message in %1$@"; + +"Chat.Reactions.MultiplePremiumTooltip" = "You can set multiple reactions with [Telegram Premium]()."; diff --git a/Tests/CallUITest/BUILD b/Tests/CallUITest/BUILD new file mode 100644 index 00000000000..a809c0ad086 --- /dev/null +++ b/Tests/CallUITest/BUILD @@ -0,0 +1,192 @@ +load("@build_bazel_rules_apple//apple:ios.bzl", + "ios_application", +) + +load("@build_bazel_rules_swift//swift:swift.bzl", + "swift_library", +) + +load("//build-system/bazel-utils:plist_fragment.bzl", + "plist_fragment", +) + +load( + "@rules_xcodeproj//xcodeproj:defs.bzl", + "top_level_target", + "top_level_targets", + "xcodeproj", + "xcode_provisioning_profile", +) + +load("@build_bazel_rules_apple//apple:apple.bzl", "local_provisioning_profile") + +load( + "@build_configuration//:variables.bzl", + "telegram_bazel_path", +) + +filegroup( + name = "AppResources", + srcs = glob([ + "Resources/**/*", + ], exclude = ["Resources/**/.*"]), +) + +swift_library( + name = "Lib", + srcs = glob([ + "Sources/**/*.swift", + ]), + data = [ + ":AppResources", + ], + deps = [ + "//submodules/Display", + "//submodules/MetalEngine", + "//submodules/TelegramUI/Components/Calls/CallScreen", + ], +) + +plist_fragment( + name = "BuildNumberInfoPlist", + extension = "plist", + template = + """ + CFBundleVersion + 1 + """ +) + +plist_fragment( + name = "VersionInfoPlist", + extension = "plist", + template = + """ + CFBundleShortVersionString + 1.0 + """ +) + +plist_fragment( + name = "AppNameInfoPlist", + extension = "plist", + template = + """ + CFBundleDisplayName + Test + """ +) + +plist_fragment( + name = "AppInfoPlist", + extension = "plist", + template = + """ + CFBundleAllowMixedLocalizations + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Test + CFBundleIdentifier + org.telegram.Telegram-iOS + CFBundleName + Telegram + CFBundlePackageType + APPL + CFBundleSignature + ???? + ITSAppUsesNonExemptEncryption + + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + UIDeviceFamily + + 1 + 2 + + UIFileSharingEnabled + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UIStatusBarStyle + UIStatusBarStyleDefault + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + UIViewEdgeAntialiasing + + UIViewGroupOpacity + + CADisableMinimumFrameDurationOnPhone + + """ +) + +ios_application( + name = "CallUITest", + bundle_id = "org.telegram.Telegram-iOS", + families = ["iphone", "ipad"], + minimum_os_version = "12.0", + provisioning_profile = "@build_configuration//provisioning:Telegram.mobileprovision", + infoplists = [ + ":AppInfoPlist", + ":BuildNumberInfoPlist", + ":VersionInfoPlist", + ], + resources = [ + "//Tests/Common:LaunchScreen", + ], + frameworks = [ + ], + deps = [ + "//Tests/Common:Main", + ":Lib", + ], + visibility = ["//visibility:public"], +) + +xcodeproj( + name = "CallUITest_xcodeproj", + build_mode = "bazel", + bazel_path = telegram_bazel_path, + project_name = "CallUITest", + tags = ["manual"], + top_level_targets = top_level_targets( + labels = [ + ":CallUITest", + ], + target_environments = ["device", "simulator"], + ), + xcode_configurations = { + "Debug": { + "//command_line_option:compilation_mode": "dbg", + }, + "Release": { + "//command_line_option:compilation_mode": "opt", + }, + }, + default_xcode_configuration = "Debug" + +) diff --git a/Tests/CallUITest/Resources/test.png b/Tests/CallUITest/Resources/test.png new file mode 100644 index 00000000000..59ef68aabd0 Binary files /dev/null and b/Tests/CallUITest/Resources/test.png differ diff --git a/Tests/CallUITest/Resources/test2.mp4 b/Tests/CallUITest/Resources/test2.mp4 new file mode 100644 index 00000000000..c0327e91e34 Binary files /dev/null and b/Tests/CallUITest/Resources/test2.mp4 differ diff --git a/Tests/CallUITest/Sources/AppDelegate.swift b/Tests/CallUITest/Sources/AppDelegate.swift new file mode 100644 index 00000000000..f40c8894f52 --- /dev/null +++ b/Tests/CallUITest/Sources/AppDelegate.swift @@ -0,0 +1,21 @@ +import Foundation +import UIKit + +@objc(Application) +public final class Application: UIApplication { +} + +@objc(AppDelegate) +public final class AppDelegate: NSObject, UIApplicationDelegate { + public var window: UIWindow? + + public func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { + let window = UIWindow() + self.window = window + + window.rootViewController = ViewController() + window.makeKeyAndVisible() + + return true + } +} diff --git a/Tests/CallUITest/Sources/ViewController.swift b/Tests/CallUITest/Sources/ViewController.swift new file mode 100644 index 00000000000..d9103d2dad3 --- /dev/null +++ b/Tests/CallUITest/Sources/ViewController.swift @@ -0,0 +1,127 @@ +import Foundation +import UIKit +import MetalEngine +import Display +import CallScreen +import ComponentFlow + +public final class ViewController: UIViewController { + private var callScreenView: PrivateCallScreen? + private var callState: PrivateCallScreen.State = PrivateCallScreen.State( + lifecycleState: .connecting, + name: "Emma Walters", + avatarImage: UIImage(named: "test"), + audioOutput: .internalSpeaker, + isMicrophoneMuted: false, + localVideo: nil, + remoteVideo: nil + ) + + override public func viewDidLoad() { + super.viewDidLoad() + + self.view.layer.addSublayer(MetalEngine.shared.rootLayer) + MetalEngine.shared.rootLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: -101.0), size: CGSize(width: 100.0, height: 100.0)) + + self.view.backgroundColor = .black + + SharedDisplayLinkDriver.shared.updateForegroundState(true) + + let callScreenView = PrivateCallScreen(frame: self.view.bounds) + self.callScreenView = callScreenView + self.view.addSubview(callScreenView) + + callScreenView.speakerAction = { [weak self] in + guard let self else { + return + } + + switch self.callState.lifecycleState { + case .connecting: + self.callState.lifecycleState = .ringing + case .ringing: + self.callState.lifecycleState = .exchangingKeys + case .exchangingKeys: + self.callState.lifecycleState = .active(PrivateCallScreen.State.ActiveState( + startTime: Date().timeIntervalSince1970, + signalInfo: PrivateCallScreen.State.SignalInfo(quality: 1.0), + emojiKey: ["A", "B", "C", "D"] + )) + case var .active(activeState): + activeState.signalInfo.quality = activeState.signalInfo.quality == 1.0 ? 0.1 : 1.0 + self.callState.lifecycleState = .active(activeState) + case .terminated: + self.callState.lifecycleState = .active(PrivateCallScreen.State.ActiveState( + startTime: Date().timeIntervalSince1970, + signalInfo: PrivateCallScreen.State.SignalInfo(quality: 1.0), + emojiKey: ["A", "B", "C", "D"] + )) + } + + self.update(transition: .spring(duration: 0.4)) + } + callScreenView.flipCameraAction = { [weak self] in + guard let self else { + return + } + if let input = self.callState.localVideo as? FileVideoSource { + input.sourceId = input.sourceId == 0 ? 1 : 0 + } + } + callScreenView.videoAction = { [weak self] in + guard let self else { + return + } + if self.callState.localVideo == nil { + self.callState.localVideo = FileVideoSource(device: MetalEngine.shared.device, url: Bundle.main.url(forResource: "test2", withExtension: "mp4")!) + } else { + self.callState.localVideo = nil + } + self.update(transition: .spring(duration: 0.4)) + } + callScreenView.microhoneMuteAction = { + if self.callState.remoteVideo == nil { + self.callState.remoteVideo = FileVideoSource(device: MetalEngine.shared.device, url: Bundle.main.url(forResource: "test2", withExtension: "mp4")!) + } else { + self.callState.remoteVideo = nil + } + self.update(transition: .spring(duration: 0.4)) + } + callScreenView.endCallAction = { [weak self] in + guard let self else { + return + } + self.callState.lifecycleState = .terminated(PrivateCallScreen.State.TerminatedState(duration: 82.0)) + self.callState.remoteVideo = nil + self.callState.localVideo = nil + self.update(transition: .spring(duration: 0.4)) + } + + self.update(transition: .immediate) + } + + private func update(transition: Transition) { + self.update(size: self.view.bounds.size, transition: transition) + } + + private func update(size: CGSize, transition: Transition) { + guard let callScreenView = self.callScreenView else { + return + } + + transition.setFrame(view: callScreenView, frame: CGRect(origin: CGPoint(), size: size)) + let insets: UIEdgeInsets + if size.width < size.height { + insets = UIEdgeInsets(top: 44.0, left: 0.0, bottom: 0.0, right: 0.0) + } else { + insets = UIEdgeInsets(top: 0.0, left: 44.0, bottom: 0.0, right: 44.0) + } + callScreenView.update(size: size, insets: insets, screenCornerRadius: 55.0, state: self.callState, transition: transition) + } + + override public func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + + self.update(size: size, transition: .easeInOut(duration: 0.3)) + } +} diff --git a/build-system/Make/ProjectGeneration.py b/build-system/Make/ProjectGeneration.py index 37e2ca22bca..a8759a89e21 100644 --- a/build-system/Make/ProjectGeneration.py +++ b/build-system/Make/ProjectGeneration.py @@ -20,19 +20,24 @@ def generate_xcodeproj(build_environment: BuildEnvironment, disable_extensions, app_target_clean = app_target.replace('/', '_') bazel_generate_arguments = [build_environment.bazel_path] - bazel_generate_arguments += ['run', '//Telegram:Telegram_xcodeproj'] + + bazel_generate_arguments += ['run', '//{}_xcodeproj'.format(app_target_spec)] bazel_generate_arguments += ['--override_repository=build_configuration={}'.format(configuration_path)] - if disable_extensions: - bazel_generate_arguments += ['--//{}:disableExtensions'.format(app_target)] - bazel_generate_arguments += ['--//{}:disableStripping'.format('Telegram')] + + if target_name == 'Telegram': + if disable_extensions: + bazel_generate_arguments += ['--//{}:disableExtensions'.format(app_target)] + bazel_generate_arguments += ['--//{}:disableStripping'.format(app_target)] project_bazel_arguments = [] for argument in bazel_app_arguments: project_bazel_arguments.append(argument) project_bazel_arguments += ['--override_repository=build_configuration={}'.format(configuration_path)] - if disable_extensions: - project_bazel_arguments += ['--//{}:disableExtensions'.format(app_target)] - project_bazel_arguments += ['--//{}:disableStripping'.format('Telegram')] + + if target_name == 'Telegram': + if disable_extensions: + project_bazel_arguments += ['--//{}:disableExtensions'.format(app_target)] + project_bazel_arguments += ['--//{}:disableStripping'.format(app_target)] project_bazel_arguments += ['--features=-swift.debug_prefix_map'] @@ -45,7 +50,7 @@ def generate_xcodeproj(build_environment: BuildEnvironment, disable_extensions, call_executable(bazel_generate_arguments) - xcodeproj_path = 'Telegram/Telegram.xcodeproj' + xcodeproj_path = '{}.xcodeproj'.format(app_target_spec.replace(':', '/')) call_executable(['open', xcodeproj_path]) diff --git a/build-system/bazel-rules/rules_xcodeproj b/build-system/bazel-rules/rules_xcodeproj index 44f7526fdae..facad433796 160000 --- a/build-system/bazel-rules/rules_xcodeproj +++ b/build-system/bazel-rules/rules_xcodeproj @@ -1 +1 @@ -Subproject commit 44f7526fdaecfd1b34c5698407e767d77be7b42c +Subproject commit facad433796cf2e71a523d0cd09a29a96b9b4890 diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 77e0c1f5a45..f9016422101 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -543,6 +543,7 @@ public enum PeerInfoControllerMode { case group(PeerId) case reaction(MessageId) case forumTopic(thread: ChatReplyThreadMessage) + case recommendedChannels } public enum ContactListActionItemInlineIconPosition { @@ -806,12 +807,30 @@ public struct StoryCameraTransitionInCoordinator { } } +public class MediaEditorTransitionOutExternalState { + public var storyTarget: Stories.PendingTarget? + public var isPeerArchived: Bool + public var transitionOut: ((Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut?)? + + public init(storyTarget: Stories.PendingTarget?, isPeerArchived: Bool, transitionOut: ((Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut?)?) { + self.storyTarget = storyTarget + self.isPeerArchived = isPeerArchived + self.transitionOut = transitionOut + } +} + +public protocol MediaEditorScreenResult { + +} + public protocol TelegramRootControllerInterface: NavigationController { @discardableResult func openStoryCamera(customTarget: EnginePeer.Id?, transitionIn: StoryCameraTransitionIn?, transitionedIn: @escaping () -> Void, transitionOut: @escaping (Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut?) -> StoryCameraTransitionInCoordinator? + func proceedWithStoryUpload(target: Stories.PendingTarget, result: MediaEditorScreenResult, existingMedia: EngineMedia?, forwardInfo: Stories.PendingForwardInfo?, externalState: MediaEditorTransitionOutExternalState, commit: @escaping (@escaping () -> Void) -> Void) func getContactsController() -> ViewController? - func getChatsController() -> ViewController? + func getChatsController() -> ViewController? + func openSettings() } public protocol SharedAccountContext: AnyObject { @@ -870,7 +889,19 @@ public protocol SharedAccountContext: AnyObject { func makeComposeController(context: AccountContext) -> ViewController func makeChatListController(context: AccountContext, location: ChatListControllerLocation, controlsHistoryPreload: Bool, hideNetworkActivityStatus: Bool, previewing: Bool, enableDebugActions: Bool) -> ChatListController func makeChatController(context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject?, botStart: ChatControllerInitialBotStart?, mode: ChatControllerPresentationMode) -> ChatController - func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)?, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?, isCentered: Bool) -> ListViewItem + func makeChatHistoryListNode( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal), + chatLocation: ChatLocation, + chatLocationContextHolder: Atomic, + tagMask: MessageTags?, + source: ChatHistoryListSource, + subject: ChatControllerSubject?, + controllerInteraction: ChatControllerInteractionProtocol, + selectedMessages: Signal?, NoError>, + mode: ChatHistoryListMode + ) -> ChatHistoryListNode + func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)?, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?, accountPeer: Peer?, isCentered: Bool) -> ListViewItem func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader func makePeerSharedMediaController(context: AccountContext, peerId: PeerId) -> ViewController? func makeContactSelectionController(_ params: ContactSelectionControllerParams) -> ContactSelectionController @@ -899,7 +930,7 @@ public protocol SharedAccountContext: AnyObject { func chatAvailableMessageActions(engine: TelegramEngine, accountPeerId: EnginePeer.Id, messageIds: Set, messages: [EngineMessage.Id: EngineMessage], peers: [EnginePeer.Id: EnginePeer]) -> Signal func resolveUrl(context: AccountContext, peerId: PeerId?, url: String, skipUrlAuth: Bool) -> Signal func resolveUrlWithProgress(context: AccountContext, peerId: PeerId?, url: String, skipUrlAuth: Bool) -> Signal - func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, forceExternal: Bool, openPeer: @escaping (EnginePeer, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)?, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?, progress: Promise?) + func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, forceExternal: Bool, openPeer: @escaping (EnginePeer, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)?, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?, progress: Promise?, completion: (() -> Void)?) func openAddContact(context: AccountContext, firstName: String, lastName: String, phoneNumber: String, label: String, present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, completed: @escaping () -> Void) func openAddPersonContact(context: AccountContext, peerId: PeerId, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void) func presentContactsWarningSuppression(context: AccountContext, present: (ViewController, Any?) -> Void) @@ -910,7 +941,7 @@ public protocol SharedAccountContext: AnyObject { func makeRecentSessionsController(context: AccountContext, activeSessionsContext: ActiveSessionsContext) -> ViewController & RecentSessionsController - func makeChatQrCodeScreen(context: AccountContext, peer: Peer, threadId: Int64?) -> ViewController + func makeChatQrCodeScreen(context: AccountContext, peer: Peer, threadId: Int64?, temporary: Bool) -> ViewController func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource, forceDark: Bool, dismissed: (() -> Void)?) -> ViewController func makePremiumDemoController(context: AccountContext, subject: PremiumDemoSubject, action: @escaping () -> Void) -> ViewController @@ -926,7 +957,9 @@ public protocol SharedAccountContext: AnyObject { func makeInstalledStickerPacksController(context: AccountContext, mode: InstalledStickerPacksControllerMode, forceTheme: PresentationTheme?) -> ViewController - func makeChannelStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, peerId: EnginePeer.Id, boosts: Bool, boostStatus: ChannelBoostStatus?, statsDatacenterId: Int32) -> ViewController + func makeChannelStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, peerId: EnginePeer.Id, boosts: Bool, boostStatus: ChannelBoostStatus?) -> ViewController + func makeMessagesStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, messageId: EngineMessage.Id) -> ViewController + func makeStoryStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, peerId: EnginePeer.Id, storyId: Int32, storyItem: EngineStoryItem, fromStory: Bool) -> ViewController func makeDebugSettingsController(context: AccountContext?) -> ViewController? @@ -975,6 +1008,8 @@ public enum PremiumIntroSource { case storiesSuggestedReactions case channelBoost(EnginePeer.Id) case nameColor + case similarChannels + case wallpapers } public enum PremiumDemoSubject { @@ -993,6 +1028,8 @@ public enum PremiumDemoSubject { case emojiStatus case translation case stories + case colors + case wallpapers } public enum PremiumLimitSubject { @@ -1070,7 +1107,7 @@ public protocol AccountContext: AnyObject { public struct PremiumConfiguration { public static var defaultValue: PremiumConfiguration { - return PremiumConfiguration(isPremiumDisabled: false, showPremiumGiftInAttachMenu: false, showPremiumGiftInTextField: false, giveawayGiftsPurchaseAvailable: false, boostsPerGiftCount: 3, minChannelNameColorLevel: 5) + return PremiumConfiguration(isPremiumDisabled: false, showPremiumGiftInAttachMenu: false, showPremiumGiftInTextField: false, giveawayGiftsPurchaseAvailable: false, boostsPerGiftCount: 3, minChannelNameColorLevel: 5, audioTransciptionTrialMaxDuration: 300, audioTransciptionTrialCount: 2) } public let isPremiumDisabled: Bool @@ -1079,14 +1116,18 @@ public struct PremiumConfiguration { public let giveawayGiftsPurchaseAvailable: Bool public let boostsPerGiftCount: Int32 public let minChannelNameColorLevel: Int32 + public let audioTransciptionTrialMaxDuration: Int32 + public let audioTransciptionTrialCount: Int32 - fileprivate init(isPremiumDisabled: Bool, showPremiumGiftInAttachMenu: Bool, showPremiumGiftInTextField: Bool, giveawayGiftsPurchaseAvailable: Bool, boostsPerGiftCount: Int32, minChannelNameColorLevel: Int32) { + fileprivate init(isPremiumDisabled: Bool, showPremiumGiftInAttachMenu: Bool, showPremiumGiftInTextField: Bool, giveawayGiftsPurchaseAvailable: Bool, boostsPerGiftCount: Int32, minChannelNameColorLevel: Int32, audioTransciptionTrialMaxDuration: Int32, audioTransciptionTrialCount: Int32) { self.isPremiumDisabled = isPremiumDisabled self.showPremiumGiftInAttachMenu = showPremiumGiftInAttachMenu self.showPremiumGiftInTextField = showPremiumGiftInTextField self.giveawayGiftsPurchaseAvailable = giveawayGiftsPurchaseAvailable self.boostsPerGiftCount = boostsPerGiftCount self.minChannelNameColorLevel = minChannelNameColorLevel + self.audioTransciptionTrialMaxDuration = audioTransciptionTrialMaxDuration + self.audioTransciptionTrialCount = audioTransciptionTrialCount } public static func with(appConfiguration: AppConfiguration) -> PremiumConfiguration { @@ -1097,7 +1138,9 @@ public struct PremiumConfiguration { showPremiumGiftInTextField: data["premium_gift_text_field_icon"] as? Bool ?? false, giveawayGiftsPurchaseAvailable: data["giveaway_gifts_purchase_available"] as? Bool ?? false, boostsPerGiftCount: Int32(data["boosts_per_sent_gift"] as? Double ?? 3), - minChannelNameColorLevel: Int32(data["channel_color_level_min"] as? Double ?? 5) + minChannelNameColorLevel: Int32(data["channel_color_level_min"] as? Double ?? 5), + audioTransciptionTrialMaxDuration: Int32(data["transcribe_audio_trial_duration_max"] as? Double ?? 300), + audioTransciptionTrialCount: Int32(data["transcribe_audio_trial_weekly_number"] as? Double ?? 2) ) } else { return .defaultValue @@ -1212,7 +1255,32 @@ public struct StickersSearchConfiguration { } } +private extension PeerNameColors.Colors { + init?(colors: EngineAvailableColorOptions.MultiColorPack) { + if colors.colors.isEmpty { + return nil + } + self.main = UIColor(rgb: colors.colors[0]) + if colors.colors.count > 1 { + self.secondary = UIColor(rgb: colors.colors[1]) + } else { + self.secondary = nil + } + if colors.colors.count > 2 { + self.tertiary = UIColor(rgb: colors.colors[2]) + } else { + self.tertiary = nil + } + } +} + public class PeerNameColors: Equatable { + public enum Subject { + case background + case palette + case stories + } + public struct Colors: Equatable { public let main: UIColor public let secondary: UIColor? @@ -1264,7 +1332,14 @@ public class PeerNameColors: Equatable { return PeerNameColors( colors: defaultSingleColors, darkColors: [:], - displayOrder: [5, 3, 1, 0, 2, 4, 6] + displayOrder: [5, 3, 1, 0, 2, 4, 6], + profileColors: [:], + profileDarkColors: [:], + profilePaletteColors: [:], + profilePaletteDarkColors: [:], + profileStoryColors: [:], + profileStoryDarkColors: [:], + profileDisplayOrder: [] ) } @@ -1272,6 +1347,14 @@ public class PeerNameColors: Equatable { public let darkColors: [Int32: Colors] public let displayOrder: [Int32] + public let profileColors: [Int32: Colors] + public let profileDarkColors: [Int32: Colors] + public let profilePaletteColors: [Int32: Colors] + public let profilePaletteDarkColors: [Int32: Colors] + public let profileStoryColors: [Int32: Colors] + public let profileStoryDarkColors: [Int32: Colors] + public let profileDisplayOrder: [Int32] + public func get(_ color: PeerNameColor, dark: Bool = false) -> Colors { if dark, let colors = self.darkColors[color.rawValue] { return colors @@ -1282,55 +1365,133 @@ public class PeerNameColors: Equatable { } } - fileprivate init(colors: [Int32: Colors], darkColors: [Int32: Colors], displayOrder: [Int32]) { + public func getProfile(_ color: PeerNameColor, dark: Bool = false, subject: Subject = .background) -> Colors { + switch subject { + case .background: + if dark, let colors = self.profileDarkColors[color.rawValue] { + return colors + } else if let colors = self.profileColors[color.rawValue] { + return colors + } else { + return Colors(main: UIColor(rgb: 0xcc5049)) + } + case .palette: + if dark, let colors = self.profilePaletteDarkColors[color.rawValue] { + return colors + } else if let colors = self.profilePaletteColors[color.rawValue] { + return colors + } else { + return self.getProfile(color, dark: dark, subject: .background) + } + case .stories: + if dark, let colors = self.profileStoryDarkColors[color.rawValue] { + return colors + } else if let colors = self.profileStoryColors[color.rawValue] { + return colors + } else { + return self.getProfile(color, dark: dark, subject: .background) + } + } + } + + fileprivate init( + colors: [Int32: Colors], + darkColors: [Int32: Colors], + displayOrder: [Int32], + profileColors: [Int32: Colors], + profileDarkColors: [Int32: Colors], + profilePaletteColors: [Int32: Colors], + profilePaletteDarkColors: [Int32: Colors], + profileStoryColors: [Int32: Colors], + profileStoryDarkColors: [Int32: Colors], + profileDisplayOrder: [Int32] + ) { self.colors = colors self.darkColors = darkColors self.displayOrder = displayOrder + self.profileColors = profileColors + self.profileDarkColors = profileDarkColors + self.profilePaletteColors = profilePaletteColors + self.profilePaletteDarkColors = profilePaletteDarkColors + self.profileStoryColors = profileStoryColors + self.profileStoryDarkColors = profileStoryDarkColors + self.profileDisplayOrder = profileDisplayOrder } - public static func with(appConfiguration: AppConfiguration) -> PeerNameColors { - if let data = appConfiguration.data { - var colors = PeerNameColors.defaultSingleColors - var darkColors: [Int32: Colors] = [:] - - if let peerColors = data["peer_colors"] as? [String: [String]] { - for (key, values) in peerColors { - if let index = Int32(key) { - let colorsArray = values.compactMap { UIColor(hexString: $0) } - if let colorValues = Colors(colors: colorsArray) { - colors[index] = colorValues - } + public static func with(availableReplyColors: EngineAvailableColorOptions, availableProfileColors: EngineAvailableColorOptions) -> PeerNameColors { + var colors: [Int32: Colors] = [:] + var darkColors: [Int32: Colors] = [:] + var displayOrder: [Int32] = [] + var profileColors: [Int32: Colors] = [:] + var profileDarkColors: [Int32: Colors] = [:] + var profilePaletteColors: [Int32: Colors] = [:] + var profilePaletteDarkColors: [Int32: Colors] = [:] + var profileStoryColors: [Int32: Colors] = [:] + var profileStoryDarkColors: [Int32: Colors] = [:] + var profileDisplayOrder: [Int32] = [] + + if !availableReplyColors.options.isEmpty { + for option in availableReplyColors.options { + if let parsedLight = PeerNameColors.Colors(colors: option.value.light.background) { + colors[option.key] = parsedLight + } + if let parsedDark = (option.value.dark?.background).flatMap(PeerNameColors.Colors.init(colors:)) { + darkColors[option.key] = parsedDark + } + + for option in availableReplyColors.options { + if !displayOrder.contains(option.key) { + displayOrder.append(option.key) } } } + } else { + let defaultValue = PeerNameColors.defaultValue + colors = defaultValue.colors + darkColors = defaultValue.darkColors + displayOrder = defaultValue.displayOrder + } - if let darkPeerColors = data["dark_peer_colors"] as? [String: [String]] { - for (key, values) in darkPeerColors { - if let index = Int32(key) { - let colorsArray = values.compactMap { UIColor(hexString: $0) } - if let colorValues = Colors(colors: colorsArray) { - darkColors[index] = colorValues - } + if !availableProfileColors.options.isEmpty { + for option in availableProfileColors.options { + if let parsedLight = PeerNameColors.Colors(colors: option.value.light.background) { + profileColors[option.key] = parsedLight + } + if let parsedDark = (option.value.dark?.background).flatMap(PeerNameColors.Colors.init(colors:)) { + profileDarkColors[option.key] = parsedDark + } + if let parsedPaletteLight = PeerNameColors.Colors(colors: option.value.light.palette) { + profilePaletteColors[option.key] = parsedPaletteLight + } + if let parsedPaletteDark = (option.value.dark?.palette).flatMap(PeerNameColors.Colors.init(colors:)) { + profilePaletteDarkColors[option.key] = parsedPaletteDark + } + if let parsedStoryLight = (option.value.light.stories).flatMap(PeerNameColors.Colors.init(colors:)) { + profileStoryColors[option.key] = parsedStoryLight + } + if let parsedStoryDark = (option.value.dark?.stories).flatMap(PeerNameColors.Colors.init(colors:)) { + profileStoryDarkColors[option.key] = parsedStoryDark + } + for option in availableProfileColors.options { + if !profileDisplayOrder.contains(option.key) { + profileDisplayOrder.append(option.key) } } } - - var displayOrder: [Int32] = [] - if let order = data["peer_colors_available"] as? [Double] { - displayOrder = order.map { Int32($0) } - } - if displayOrder.isEmpty { - displayOrder = PeerNameColors.defaultValue.displayOrder - } - - return PeerNameColors( - colors: colors, - darkColors: darkColors, - displayOrder: displayOrder - ) - } else { - return .defaultValue } + + return PeerNameColors( + colors: colors, + darkColors: darkColors, + displayOrder: displayOrder, + profileColors: profileColors, + profileDarkColors: profileDarkColors, + profilePaletteColors: profilePaletteColors, + profilePaletteDarkColors: profilePaletteDarkColors, + profileStoryColors: profileStoryColors, + profileStoryDarkColors: profileStoryDarkColors, + profileDisplayOrder: profileDisplayOrder + ) } public static func == (lhs: PeerNameColors, rhs: PeerNameColors) -> Bool { @@ -1343,6 +1504,27 @@ public class PeerNameColors: Equatable { if lhs.displayOrder != rhs.displayOrder { return false } + if lhs.profileColors != rhs.profileColors { + return false + } + if lhs.profileDarkColors != rhs.profileDarkColors { + return false + } + if lhs.profilePaletteColors != rhs.profilePaletteColors { + return false + } + if lhs.profilePaletteDarkColors != rhs.profilePaletteDarkColors { + return false + } + if lhs.profileStoryColors != rhs.profileStoryColors { + return false + } + if lhs.profileStoryDarkColors != rhs.profileStoryDarkColors { + return false + } + if lhs.profileDisplayOrder != rhs.profileDisplayOrder { + return false + } return true } } diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index f6ce30c5ba6..7e8a7d5b80c 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -50,8 +50,35 @@ public final class ChatMessageItemAssociatedData: Equatable { public let hasBots: Bool public let translateToLanguage: String? public let maxReadStoryId: Int32? - - public init(automaticDownloadPeerType: MediaAutoDownloadPeerType, automaticDownloadPeerId: EnginePeer.Id?, automaticDownloadNetworkType: MediaAutoDownloadNetworkType, isRecentActions: Bool = false, subject: ChatControllerSubject? = nil, contactsPeerIds: Set = Set(), channelDiscussionGroup: ChannelDiscussionGroupStatus = .unknown, animatedEmojiStickers: [String: [StickerPackItem]] = [:], additionalAnimatedEmojiStickers: [String: [Int: StickerPackItem]] = [:], forcedResourceStatus: FileMediaResourceStatus? = nil, currentlyPlayingMessageId: EngineMessage.Index? = nil, isCopyProtectionEnabled: Bool = false, availableReactions: AvailableReactions?, defaultReaction: MessageReaction.Reaction?, isPremium: Bool, accountPeer: EnginePeer?, forceInlineReactions: Bool = false, alwaysDisplayTranscribeButton: DisplayTranscribeButton = DisplayTranscribeButton(canBeDisplayed: false, displayForNotConsumed: false), topicAuthorId: EnginePeer.Id? = nil, hasBots: Bool = false, translateToLanguage: String? = nil, maxReadStoryId: Int32? = nil) { + public let recommendedChannels: RecommendedChannels? + public let audioTranscriptionTrial: AudioTranscription.TrialState + + public init( + automaticDownloadPeerType: MediaAutoDownloadPeerType, + automaticDownloadPeerId: EnginePeer.Id?, + automaticDownloadNetworkType: MediaAutoDownloadNetworkType, + isRecentActions: Bool = false, + subject: ChatControllerSubject? = nil, + contactsPeerIds: Set = Set(), + channelDiscussionGroup: ChannelDiscussionGroupStatus = .unknown, + animatedEmojiStickers: [String: [StickerPackItem]] = [:], + additionalAnimatedEmojiStickers: [String: [Int: StickerPackItem]] = [:], + forcedResourceStatus: FileMediaResourceStatus? = nil, + currentlyPlayingMessageId: EngineMessage.Index? = nil, + isCopyProtectionEnabled: Bool = false, + availableReactions: AvailableReactions?, + defaultReaction: MessageReaction.Reaction?, + isPremium: Bool, + accountPeer: EnginePeer?, + forceInlineReactions: Bool = false, + alwaysDisplayTranscribeButton: DisplayTranscribeButton = DisplayTranscribeButton(canBeDisplayed: false, displayForNotConsumed: false), + topicAuthorId: EnginePeer.Id? = nil, + hasBots: Bool = false, + translateToLanguage: String? = nil, + maxReadStoryId: Int32? = nil, + recommendedChannels: RecommendedChannels? = nil, + audioTranscriptionTrial: AudioTranscription.TrialState = .defaultValue + ) { self.automaticDownloadPeerType = automaticDownloadPeerType self.automaticDownloadPeerId = automaticDownloadPeerId self.automaticDownloadNetworkType = automaticDownloadNetworkType @@ -74,6 +101,8 @@ public final class ChatMessageItemAssociatedData: Equatable { self.hasBots = hasBots self.translateToLanguage = translateToLanguage self.maxReadStoryId = maxReadStoryId + self.recommendedChannels = recommendedChannels + self.audioTranscriptionTrial = audioTranscriptionTrial } public static func == (lhs: ChatMessageItemAssociatedData, rhs: ChatMessageItemAssociatedData) -> Bool { @@ -140,6 +169,12 @@ public final class ChatMessageItemAssociatedData: Equatable { if lhs.maxReadStoryId != rhs.maxReadStoryId { return false } + if lhs.recommendedChannels != rhs.recommendedChannels { + return false + } + if lhs.audioTranscriptionTrial != rhs.audioTranscriptionTrial { + return false + } return true } } @@ -226,9 +261,17 @@ public struct ChatControllerInitialBotAppStart { } public enum ChatControllerInteractionNavigateToPeer { + public struct InfoParams { + public let switchToRecommendedChannels: Bool + + public init(switchToRecommendedChannels: Bool) { + self.switchToRecommendedChannels = switchToRecommendedChannels + } + } + case `default` case chat(textInputState: ChatTextInputState?, subject: ChatControllerSubject?, peekData: ChatPeekTimeout?) - case info + case info(InfoParams?) case withBotStartPayload(ChatControllerInitialBotStart) case withAttachBot(ChatControllerInitialAttachBotStart) case withBotApp(ChatControllerInitialBotAppStart) @@ -578,10 +621,12 @@ public enum ChatControllerSubject: Equatable { public struct Quote: Equatable { public let messageId: EngineMessage.Id public let text: String + public let offset: Int? - public init(messageId: EngineMessage.Id, text: String) { + public init(messageId: EngineMessage.Id, text: String, offset: Int?) { self.messageId = messageId self.text = text + self.offset = offset } } @@ -645,9 +690,19 @@ public enum ChatControllerSubject: Equatable { } public struct MessageHighlight: Equatable { - public var quote: String? + public struct Quote: Equatable { + public var string: String + public var offset: Int? + + public init(string: String, offset: Int?) { + self.string = string + self.offset = offset + } + } + + public var quote: Quote? - public init(quote: String? = nil) { + public init(quote: Quote? = nil) { self.quote = quote } } @@ -685,6 +740,15 @@ public enum ChatControllerSubject: Equatable { } } } + + public var isService: Bool { + switch self { + case .message: + return false + default: + return true + } + } } public enum ChatControllerPresentationMode: Equatable { @@ -845,6 +909,8 @@ public protocol ChatController: ViewController { var isSelectingMessagesUpdated: ((Bool) -> Void)? { get set } func cancelSelectingMessages() + func activateSearch(domain: ChatSearchDomain, query: String) + func beginClearHistory(type: InteractiveHistoryClearingType) } public protocol ChatMessagePreviewItemNode: AnyObject { @@ -876,3 +942,80 @@ public protocol ChatMessageItemNodeProtocol: ListViewItemNode { func targetForStoryTransition(id: StoryId) -> UIView? func contentFrame() -> CGRect } + +public final class ChatControllerNavigationData: CustomViewControllerNavigationData { + public let peerId: PeerId + public let threadId: Int64? + + public init(peerId: PeerId, threadId: Int64?) { + self.peerId = peerId + self.threadId = threadId + } + + public func combine(summary: CustomViewControllerNavigationDataSummary?) -> CustomViewControllerNavigationDataSummary? { + if let summary = summary as? ChatControllerNavigationDataSummary { + return summary.adding(peerNavigationItem: ChatNavigationStackItem(peerId: self.peerId, threadId: threadId)) + } else { + return ChatControllerNavigationDataSummary(peerNavigationItems: [ChatNavigationStackItem(peerId: self.peerId, threadId: threadId)]) + } + } +} + +public final class ChatControllerNavigationDataSummary: CustomViewControllerNavigationDataSummary { + public let peerNavigationItems: [ChatNavigationStackItem] + + public init(peerNavigationItems: [ChatNavigationStackItem]) { + self.peerNavigationItems = peerNavigationItems + } + + public func adding(peerNavigationItem: ChatNavigationStackItem) -> ChatControllerNavigationDataSummary { + var peerNavigationItems = self.peerNavigationItems + if let index = peerNavigationItems.firstIndex(of: peerNavigationItem) { + peerNavigationItems.removeSubrange(0 ... index) + } + peerNavigationItems.insert(peerNavigationItem, at: 0) + return ChatControllerNavigationDataSummary(peerNavigationItems: peerNavigationItems) + } +} + +public enum ChatHistoryListSource { + public struct Quote { + public var text: String + public var offset: Int? + + public init(text: String, offset: Int?) { + self.text = text + self.offset = offset + } + } + + case `default` + case custom(messages: Signal<([Message], Int32, Bool), NoError>, messageId: MessageId, quote: Quote?, loadMore: (() -> Void)?) +} + +public enum ChatHistoryListDisplayHeaders { + case none + case all + case allButLast +} + +public enum ChatHistoryListMode: Equatable { + case bubbles + case list(search: Bool, reversed: Bool, reverseGroups: Bool, displayHeaders: ChatHistoryListDisplayHeaders, hintLinks: Bool, isGlobalSearch: Bool) +} + +public protocol ChatControllerInteractionProtocol: AnyObject { +} + +public enum ChatHistoryNodeHistoryState: Equatable { + case loading + case loaded(isEmpty: Bool) +} + +public protocol ChatHistoryListNode: ListView { + var historyState: ValuePromise { get } + + func scrollToEndOfHistory() + func updateLayout(transition: ContainedViewLayoutTransition, updateSizeAndInsets: ListViewUpdateSizeAndInsets) + func messageInCurrentHistoryView(_ id: MessageId) -> Message? +} diff --git a/submodules/AccountContext/Sources/ChatHistoryLocation.swift b/submodules/AccountContext/Sources/ChatHistoryLocation.swift index f85a351a724..5d84475d93c 100644 --- a/submodules/AccountContext/Sources/ChatHistoryLocation.swift +++ b/submodules/AccountContext/Sources/ChatHistoryLocation.swift @@ -8,20 +8,40 @@ public enum ChatHistoryInitialSearchLocation: Equatable { } public struct MessageHistoryScrollToSubject: Equatable { + public struct Quote: Equatable { + public var string: String + public var offset: Int? + + public init(string: String, offset: Int?) { + self.string = string + self.offset = offset + } + } + public var index: MessageHistoryAnchorIndex - public var quote: String? + public var quote: Quote? - public init(index: MessageHistoryAnchorIndex, quote: String?) { + public init(index: MessageHistoryAnchorIndex, quote: Quote?) { self.index = index self.quote = quote } } public struct MessageHistoryInitialSearchSubject: Equatable { + public struct Quote: Equatable { + public var string: String + public var offset: Int? + + public init(string: String, offset: Int?) { + self.string = string + self.offset = offset + } + } + public var location: ChatHistoryInitialSearchLocation - public var quote: String? + public var quote: Quote? - public init(location: ChatHistoryInitialSearchLocation, quote: String?) { + public init(location: ChatHistoryInitialSearchLocation, quote: Quote?) { self.location = location self.quote = quote } diff --git a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift index e49ca54d119..a65befe803f 100644 --- a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift +++ b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift @@ -191,6 +191,55 @@ final class CustomEmojiContainerView: UIView { } } +private func makeTextInputTheme(context: AccountContext, interfaceState: ChatPresentationInterfaceState) -> ChatInputTextView.Theme { + let lineStyle: ChatInputTextView.Theme.Quote.LineStyle + let authorNameColor: UIColor + + if let peer = interfaceState.renderedPeer?.peer as? TelegramChannel, case .broadcast = peer.info, let nameColor = peer.nameColor { + let colors = context.peerNameColors.get(nameColor) + authorNameColor = colors.main + + if let secondary = colors.secondary, let tertiary = colors.tertiary { + lineStyle = .tripleDashed(mainColor: colors.main, secondaryColor: secondary, tertiaryColor: tertiary) + } else if let secondary = colors.secondary { + lineStyle = .doubleDashed(mainColor: colors.main, secondaryColor: secondary) + } else { + lineStyle = .solid(color: colors.main) + } + } else if let accountPeerColor = interfaceState.accountPeerColor { + authorNameColor = interfaceState.theme.list.itemAccentColor + + switch accountPeerColor.style { + case .solid: + lineStyle = .solid(color: authorNameColor) + case .doubleDashed: + lineStyle = .doubleDashed(mainColor: authorNameColor, secondaryColor: .clear) + case .tripleDashed: + lineStyle = .tripleDashed(mainColor: authorNameColor, secondaryColor: .clear, tertiaryColor: .clear) + } + } else { + lineStyle = .solid(color: interfaceState.theme.list.itemAccentColor) + authorNameColor = interfaceState.theme.list.itemAccentColor + } + + let codeBackgroundColor: UIColor + if interfaceState.theme.overallDarkAppearance { + codeBackgroundColor = UIColor(white: 1.0, alpha: 0.05) + } else { + codeBackgroundColor = UIColor(white: 0.0, alpha: 0.05) + } + + return ChatInputTextView.Theme( + quote: ChatInputTextView.Theme.Quote( + background: authorNameColor.withMultipliedAlpha(interfaceState.theme.overallDarkAppearance ? 0.2 : 0.1), + foreground: authorNameColor, + lineStyle: lineStyle, + codeBackground: codeBackgroundColor, + codeForeground: authorNameColor + ) + ) +} + public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, ASEditableTextNodeDelegate, ChatInputTextNodeDelegate { private let context: AccountContext @@ -499,6 +548,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS private func loadTextInputNode() { let textInputNode = CaptionEditableTextNode() textInputNode.initialPrimaryLanguage = self.presentationInterfaceState?.interfaceState.inputLanguage + var textColor: UIColor = .black var tintColor: UIColor = .blue var baseFontSize: CGFloat = 17.0 @@ -508,6 +558,8 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS tintColor = presentationInterfaceState.theme.list.itemAccentColor baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) keyboardAppearance = presentationInterfaceState.theme.rootController.keyboardColor.keyboardAppearance + + textInputNode.textView.theme = makeTextInputTheme(context: self.context, interfaceState: presentationInterfaceState) } let paragraphStyle = NSMutableParagraphStyle() @@ -1536,11 +1588,21 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS } if hasSpoilers { + children.insert(UIAction(title: self.strings?.TextFormat_Quote ?? "Quote", image: nil) { [weak self] (action) in + if let strongSelf = self { + strongSelf.formatAttributesQuote(strongSelf) + } + }, at: 0) children.append(UIAction(title: self.strings?.TextFormat_Spoiler ?? "Spoiler", image: nil) { [weak self] (action) in if let strongSelf = self { strongSelf.formatAttributesSpoiler(strongSelf) } }) + children.append(UIAction(title: self.strings?.TextFormat_Code ?? "Code", image: nil) { [weak self] (action) in + if let strongSelf = self { + strongSelf.formatAttributesCodeBlock(strongSelf) + } + }) } let formatMenu = UIMenu(title: self.strings?.TextFormat_Format ?? "Format", image: nil, children: children) diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryController.swift index da9f13ea0fc..bb36ad0c57b 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryController.swift @@ -22,7 +22,7 @@ public final class AuthorizationSequenceCodeEntryController: ViewController { public var openFragment: ((String) -> Void)? var reset: (() -> Void)? - var requestNextOption: (() -> Void)? + public var requestNextOption: (() -> Void)? var resetEmail: (() -> Void)? var retryResetEmail: (() -> Void)? diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift index 3a38f602b1f..478d1922630 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift @@ -20,13 +20,16 @@ import TelegramNotices import AuthenticationServices import Markdown import AlertUI +import ObjectiveC + +private var ObjCKey_Delegate: Int? private enum InnerState: Equatable { case state(UnauthorizedAccountStateContents) case authorized } -public final class AuthorizationSequenceController: NavigationController, MFMailComposeViewControllerDelegate, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding { +public final class AuthorizationSequenceController: NavigationController, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding { static func navigationBarTheme(_ theme: PresentationTheme) -> NavigationBarTheme { return NavigationBarTheme(buttonColor: theme.intro.accentTextColor, disabledButtonColor: theme.intro.disabledTextColor, primaryTextColor: theme.intro.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: theme.rootController.navigationBar.badgeBackgroundColor, badgeStrokeColor: theme.rootController.navigationBar.badgeStrokeColor, badgeTextColor: theme.rootController.navigationBar.badgeTextColor) } @@ -216,7 +219,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail let carrier = CTCarrier() let mnc = carrier.mobileNetworkCode ?? "none" - strongSelf.presentEmailComposeController(address: "login@stel.com", subject: strongSelf.presentationData.strings.Login_InvalidPhoneEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_InvalidPhoneEmailBody(formattedNumber, appVersion, systemVersion, locale, mnc).string, from: controller) + AuthorizationSequenceController.presentEmailComposeController(address: "login@stel.com", subject: strongSelf.presentationData.strings.Login_InvalidPhoneEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_InvalidPhoneEmailBody(formattedNumber, appVersion, systemVersion, locale, mnc).string, from: controller, presentationData: strongSelf.presentationData) })) case .phoneLimitExceeded: text = strongSelf.presentationData.strings.Login_PhoneFloodError @@ -242,7 +245,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail let carrier = CTCarrier() let mnc = carrier.mobileNetworkCode ?? "none" - strongSelf.presentEmailComposeController(address: "login@stel.com", subject: strongSelf.presentationData.strings.Login_PhoneBannedEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_PhoneBannedEmailBody(formattedNumber, appVersion, systemVersion, locale, mnc).string, from: controller) + AuthorizationSequenceController.presentEmailComposeController(address: "login@stel.com", subject: strongSelf.presentationData.strings.Login_PhoneBannedEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_PhoneBannedEmailBody(formattedNumber, appVersion, systemVersion, locale, mnc).string, from: controller, presentationData: strongSelf.presentationData) })) case let .generic(info): text = strongSelf.presentationData.strings.Login_UnknownError @@ -264,7 +267,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail errorString = "unknown" } - strongSelf.presentEmailComposeController(address: "login@stel.com", subject: strongSelf.presentationData.strings.Login_PhoneGenericEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_PhoneGenericEmailBody(formattedNumber, errorString, appVersion, systemVersion, locale, mnc).string, from: controller) + AuthorizationSequenceController.presentEmailComposeController(address: "login@stel.com", subject: strongSelf.presentationData.strings.Login_PhoneGenericEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_PhoneGenericEmailBody(formattedNumber, errorString, appVersion, systemVersion, locale, mnc).string, from: controller, presentationData: strongSelf.presentationData) })) case .timeout: text = strongSelf.presentationData.strings.Login_NetworkError @@ -558,26 +561,8 @@ public final class AuthorizationSequenceController: NavigationController, MFMail controller.requestNextOption = { [weak self, weak controller] in if let strongSelf = self { if nextType == nil { - if MFMailComposeViewController.canSendMail(), let controller = controller { - let formattedNumber = formatPhoneNumber(number) - - var emailBody = "" - emailBody.append(strongSelf.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 - let carrier = CTCarrier() - let mnc = carrier.mobileNetworkCode ?? "none" - emailBody.append("Telegram: \(appVersion)\n") - emailBody.append("OS: \(systemVersion)\n") - emailBody.append("Locale: \(locale)\n") - emailBody.append("MNC: \(mnc)") - - strongSelf.presentEmailComposeController(address: "sms@telegram.org", subject: strongSelf.presentationData.strings.Login_EmailCodeSubject(formattedNumber).string, body: emailBody, from: controller) - } else { - controller?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.Login_EmailNotConfiguredError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + if let controller { + AuthorizationSequenceController.presentDidNotGetCodeUI(controller: controller, presentationData: strongSelf.presentationData, number: number) } } else { controller?.inProgress = true @@ -1259,24 +1244,29 @@ public final class AuthorizationSequenceController: NavigationController, MFMail } } - private func presentEmailComposeController(address: String, subject: String, body: String, from controller: ViewController) { + private 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?) { + controller.dismiss(animated: true, completion: nil) + } + } + let composeController = MFMailComposeViewController() composeController.setToRecipients([address]) composeController.setSubject(subject) composeController.setMessageBody(body, isHTML: false) - composeController.mailComposeDelegate = self + + let composeDelegate = ComposeDelegate() + objc_setAssociatedObject(composeDelegate, &ObjCKey_Delegate, composeDelegate, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + composeController.mailComposeDelegate = composeDelegate controller.view.window?.rootViewController?.present(composeController, animated: true, completion: nil) } else { - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Login_EmailNotConfiguredError, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + 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)) } } - public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { - controller.dismiss(animated: true, completion: nil) - } - private func animateIn() { if !self.otherAccountPhoneNumbers.1.isEmpty { self.view.layer.animatePosition(from: CGPoint(x: self.view.layer.position.x, y: self.view.layer.position.y + self.view.layer.bounds.size.height), to: self.view.layer.position, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) @@ -1333,4 +1323,28 @@ public final class AuthorizationSequenceController: NavigationController, MFMail return countryCode } + + public static func presentDidNotGetCodeUI(controller: ViewController, presentationData: PresentationData, number: String) { + if MFMailComposeViewController.canSendMail() { + let formattedNumber = formatPhoneNumber(number) + + 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 + let carrier = CTCarrier() + let mnc = carrier.mobileNetworkCode ?? "none" + 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)) + } + } } diff --git a/submodules/AvatarNode/Sources/AvatarBadgeView.swift b/submodules/AvatarNode/Sources/AvatarBadgeView.swift index 5bd34a14681..1354f9382be 100644 --- a/submodules/AvatarNode/Sources/AvatarBadgeView.swift +++ b/submodules/AvatarNode/Sources/AvatarBadgeView.swift @@ -29,6 +29,9 @@ public final class AvatarBadgeView: UIImageView { private struct Parameters: Equatable { var size: CGSize var text: String + var hasTimeoutIcon: Bool + var useSolidColor: Bool + var strokeColor: UIColor? } private var originalContent: OriginalContent? @@ -50,8 +53,8 @@ public final class AvatarBadgeView: UIImageView { } } - public func update(size: CGSize, text: String) { - let parameters = Parameters(size: size, text: text) + public func update(size: CGSize, text: String, hasTimeoutIcon: Bool = true, useSolidColor: Bool = false, strokeColor: UIColor? = nil) { + let parameters = Parameters(size: size, text: text, hasTimeoutIcon: hasTimeoutIcon, useSolidColor: useSolidColor, strokeColor: strokeColor) if self.parameters != parameters || !self.hasContent { self.parameters = parameters self.update() @@ -192,24 +195,95 @@ public final class AvatarBadgeView: UIImageView { return } - self.image = generateImage(parameters.size, rotatedContext: { size, context in + var solidColor: UIColor? + if parameters.useSolidColor { + let context = DrawingContext(size: CGSize(width: 1.0, height: 1.0), scale: 1.0, clear: false)! + context.withFlippedContext({ context in + if let cgImage = blurredImage.cgImage { + context.draw(cgImage, in: CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0)) + } + }) + solidColor = context.colorAt(.zero) + } + + var badgeSize = parameters.size + + let strokeWidth: CGFloat = 1.0 + UIScreenPixel + var size = parameters.size + var offset: CGPoint = .zero + if parameters.strokeColor != nil { + offset = CGPoint(x: strokeWidth / 2.0, y: strokeWidth / 2.0) + badgeSize.width += strokeWidth + badgeSize.height += strokeWidth + size.width += strokeWidth * 2.0 + size.height += strokeWidth * 2.0 + } + + self.image = generateImage(size, rotatedContext: { size, context in UIGraphicsPushContext(context) context.clear(CGRect(origin: CGPoint(), size: size)) - context.setBlendMode(.copy) - context.setFillColor(UIColor.black.cgColor) - context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) - - blurredImage.draw(in: CGRect(origin: CGPoint(), size: size), blendMode: .sourceIn, alpha: 1.0) + let textColor: UIColor + if parameters.useSolidColor { + textColor = .white + } else { + if isLightImage { + textColor = UIColor(white: 0.7, alpha: 1.0) + } else { + textColor = .white + } + } - context.setBlendMode(.normal) + if var solidColor { + func adjustedBackgroundColor(backgroundColor: UIColor, textColor: UIColor) -> UIColor { + let minContrastRatio: CGFloat = 4.5 + if backgroundColor.contrastRatio(with: textColor) < minContrastRatio { + var hue: CGFloat = 0 + var saturation: CGFloat = 0 + var brightness: CGFloat = 0 + var alpha: CGFloat = 0 + backgroundColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) + + if brightness > 0.7 { + brightness = brightness * 0.9 + saturation = min(saturation + 0.1, 1) + } + + return UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: alpha) + } else { + return backgroundColor + } + } + solidColor = adjustedBackgroundColor(backgroundColor: solidColor, textColor: textColor) + + if let strokeColor = parameters.strokeColor { + context.setStrokeColor(strokeColor.cgColor) + context.setLineWidth(strokeWidth) + } + + context.setFillColor(solidColor.cgColor) + } else { + context.setBlendMode(.copy) + context.setFillColor(UIColor.black.cgColor) + } + if badgeSize.width != badgeSize.height { + let path = UIBezierPath(roundedRect: CGRect(origin: offset, size: badgeSize), cornerRadius: badgeSize.height / 2.0) + context.addPath(path.cgPath) + if let _ = parameters.strokeColor { + context.drawPath(using: .fillStroke) + } else { + context.fillPath() + } + } else { + context.fillEllipse(in: CGRect(origin: offset, size: badgeSize)) + } - let textColor: UIColor - if isLightImage { - textColor = UIColor(white: 0.7, alpha: 1.0) + if let _ = solidColor { + } else { - textColor = .white + blurredImage.draw(in: CGRect(origin: CGPoint(), size: badgeSize), blendMode: .sourceIn, alpha: 1.0) + context.setBlendMode(.normal) } var fontSize: CGFloat = floor(parameters.size.height * 0.48) @@ -225,28 +299,30 @@ public final class AvatarBadgeView: UIImageView { } } - let lineWidth: CGFloat = 1.5 - let lineInset: CGFloat = 2.0 - let lineRadius: CGFloat = size.width * 0.5 - lineInset - lineWidth * 0.5 - context.setLineWidth(lineWidth) - context.setStrokeColor(textColor.cgColor) - context.setLineCap(.round) - - context.addArc(center: CGPoint(x: size.width * 0.5, y: size.height * 0.5), radius: lineRadius, startAngle: CGFloat.pi * 0.5, endAngle: -CGFloat.pi * 0.5, clockwise: false) - context.strokePath() - - let sectionAngle: CGFloat = CGFloat.pi / 11.0 - - for i in 0 ..< 10 { - if i % 2 == 0 { - continue - } - - let startAngle = CGFloat.pi * 0.5 - CGFloat(i) * sectionAngle - sectionAngle * 0.15 - let endAngle = startAngle - sectionAngle * 0.75 + if parameters.hasTimeoutIcon { + let lineWidth: CGFloat = 1.5 + let lineInset: CGFloat = 2.0 + let lineRadius: CGFloat = size.width * 0.5 - lineInset - lineWidth * 0.5 + context.setLineWidth(lineWidth) + context.setStrokeColor(textColor.cgColor) + context.setLineCap(.round) - context.addArc(center: CGPoint(x: size.width * 0.5, y: size.height * 0.5), radius: lineRadius, startAngle: startAngle, endAngle: endAngle, clockwise: true) + context.addArc(center: CGPoint(x: size.width * 0.5, y: size.height * 0.5), radius: lineRadius, startAngle: CGFloat.pi * 0.5, endAngle: -CGFloat.pi * 0.5, clockwise: false) context.strokePath() + + let sectionAngle: CGFloat = CGFloat.pi / 11.0 + + for i in 0 ..< 10 { + if i % 2 == 0 { + continue + } + + let startAngle = CGFloat.pi * 0.5 - CGFloat(i) * sectionAngle - sectionAngle * 0.15 + let endAngle = startAngle - sectionAngle * 0.75 + + context.addArc(center: CGPoint(x: size.width * 0.5, y: size.height * 0.5), radius: lineRadius, startAngle: startAngle, endAngle: endAngle, clockwise: true) + context.strokePath() + } } /*if isLightImage { diff --git a/submodules/AvatarNode/Sources/AvatarNode.swift b/submodules/AvatarNode/Sources/AvatarNode.swift index b00c4f723c1..0fdf68b1829 100644 --- a/submodules/AvatarNode/Sources/AvatarNode.swift +++ b/submodules/AvatarNode/Sources/AvatarNode.swift @@ -21,6 +21,7 @@ import DirectMediaImageCache private let deletedIcon = UIImage(bundleImageName: "Avatar/DeletedIcon")?.precomposed() private let phoneIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/PhoneIcon"), color: .white) public let savedMessagesIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/SavedMessagesIcon"), color: .white) +public let repostStoryIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/RepostStoryIcon"), color: .white) private let archivedChatsIcon = UIImage(bundleImageName: "Avatar/ArchiveAvatarIcon")?.precomposed() private let repliesIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/RepliesMessagesIcon"), color: .white) @@ -90,6 +91,8 @@ private func calculateColors(context: AccountContext?, explicitColorIndex: Int?, colors = AvatarNode.grayscaleColors } else if case .savedMessagesIcon = icon { colors = AvatarNode.savedMessagesColors + } else if case .repostIcon = icon { + colors = AvatarNode.repostColors } else if case .repliesIcon = icon { colors = AvatarNode.savedMessagesColors } else if case .editAvatarIcon = icon, let theme { @@ -176,6 +179,7 @@ private enum AvatarNodeIcon: Equatable { case editAvatarIcon case deletedIcon case phoneIcon + case repostIcon } public enum AvatarNodeImageOverride: Equatable { @@ -187,6 +191,7 @@ public enum AvatarNodeImageOverride: Equatable { case editAvatarIcon(forceNone: Bool) case deletedIcon case phoneIcon + case repostIcon } public enum AvatarNodeColorOverride { @@ -257,6 +262,10 @@ public final class AvatarNode: ASDisplayNode { UIColor(rgb: 0x2a9ef1), UIColor(rgb: 0x72d5fd) ] + static let repostColors: [UIColor] = [ + UIColor(rgb: 0x34C76F), UIColor(rgb: 0x3DA1FD) + ] + public final class ContentNode: ASDisplayNode { private struct Params: Equatable { let peerId: EnginePeer.Id? @@ -288,8 +297,6 @@ public final class AvatarNode: ASDisplayNode { private var theme: PresentationTheme? private var overrideImage: AvatarNodeImageOverride? public let imageNode: ImageNode - private var animationBackgroundNode: ImageNode? - private var animationNode: AnimationNode? public var editOverlayNode: AvatarEditOverlayNode? private let imageReadyDisposable = MetaDisposable() @@ -432,7 +439,20 @@ public final class AvatarNode: ASDisplayNode { self.imageNode.isHidden = true } - // MARK: Nicegram, nicegramImage added + public func playRepostAnimation() { + let animationNode = AnimationNode(animation: "anim_storyrepost", colors: [:], scale: 0.11) + animationNode.isUserInteractionEnabled = false + self.addSubnode(animationNode) + + if var size = animationNode.preferredSize() { + size = CGSize(width: ceil(size.width), height: ceil(size.height)) + animationNode.frame = CGRect(x: floor((self.bounds.width - size.width) / 2.0), y: floor((self.bounds.height - size.height) / 2.0) + 1.0, width: size.width, height: size.height) + Queue.mainQueue().after(0.15, { + animationNode.play() + }) + } + } + public func setPeer( accountPeerId: EnginePeer.Id, postbox: Postbox, @@ -461,6 +481,9 @@ public final class AvatarNode: ASDisplayNode { case .savedMessagesIcon: representation = nil icon = .savedMessagesIcon + case .repostIcon: + representation = nil + icon = .repostIcon case .repliesIcon: representation = nil icon = .repliesIcon @@ -626,6 +649,9 @@ public final class AvatarNode: ASDisplayNode { case .savedMessagesIcon: representation = nil icon = .savedMessagesIcon + case .repostIcon: + representation = nil + icon = .repostIcon case .repliesIcon: representation = nil icon = .repliesIcon @@ -804,7 +830,11 @@ public final class AvatarNode: ASDisplayNode { let colorsArray: NSArray = colors.map(\.cgColor) as NSArray var iconColor = UIColor.white + var diagonal = false if let parameters = parameters as? AvatarNodeParameters, parameters.icon != .none { + if case .repostIcon = parameters.icon { + diagonal = true + } if case let .archivedChatsIcon(hiddenByDefault) = parameters.icon, let theme = parameters.theme { if hiddenByDefault { iconColor = theme.chatList.unpinnedArchiveAvatarColor.foregroundColor @@ -819,7 +849,11 @@ public final class AvatarNode: ASDisplayNode { let colorSpace = CGColorSpaceCreateDeviceRGB() let gradient = CGGradient(colorsSpace: colorSpace, colors: colorsArray, locations: &locations)! - context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: bounds.size.height), options: CGGradientDrawingOptions()) + if diagonal { + context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: bounds.size.height), end: CGPoint(x: bounds.size.width, y: 0.0), options: CGGradientDrawingOptions()) + } else { + context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: bounds.size.height), options: CGGradientDrawingOptions()) + } context.setBlendMode(.normal) @@ -851,6 +885,17 @@ public final class AvatarNode: ASDisplayNode { if let savedMessagesIcon = savedMessagesIcon { context.draw(savedMessagesIcon.cgImage!, in: CGRect(origin: CGPoint(x: floor((bounds.size.width - savedMessagesIcon.size.width) / 2.0), y: floor((bounds.size.height - savedMessagesIcon.size.height) / 2.0)), size: savedMessagesIcon.size)) } + } else if case .repostIcon = parameters.icon { + if !"".isEmpty { + let factor = bounds.size.width / 60.0 + context.translateBy(x: bounds.size.width / 2.0, y: bounds.size.height / 2.0) + context.scaleBy(x: factor, y: -factor) + context.translateBy(x: -bounds.size.width / 2.0, y: -bounds.size.height / 2.0) + + if let repostStoryIcon = repostStoryIcon { + context.draw(repostStoryIcon.cgImage!, in: CGRect(origin: CGPoint(x: floor((bounds.size.width - repostStoryIcon.size.width) / 2.0), y: floor((bounds.size.height - repostStoryIcon.size.height) / 2.0)), size: repostStoryIcon.size)) + } + } } else if case .repliesIcon = parameters.icon { let factor = bounds.size.width / 60.0 context.translateBy(x: bounds.size.width / 2.0, y: bounds.size.height / 2.0) @@ -1022,6 +1067,10 @@ public final class AvatarNode: ASDisplayNode { self.contentNode.playArchiveAnimation() } + public func playRepostAnimation() { + self.contentNode.playRepostAnimation() + } + public func setPeer( accountPeerId: EnginePeer.Id, postbox: Postbox, diff --git a/submodules/CallListUI/Sources/CallListController.swift b/submodules/CallListUI/Sources/CallListController.swift index 583d63023ad..98f1cec192a 100644 --- a/submodules/CallListUI/Sources/CallListController.swift +++ b/submodules/CallListUI/Sources/CallListController.swift @@ -122,6 +122,7 @@ public final class CallListController: TelegramBaseController { self.segmentedTitleView.indexUpdated = { [weak self] index in if let strongSelf = self { + strongSelf.segmentedTitleView.index = index strongSelf.controllerNode.updateType(index == 0 ? .all : .missed) } } diff --git a/submodules/Camera/Sources/Camera.swift b/submodules/Camera/Sources/Camera.swift index f0bf380219a..ad935ca89c1 100644 --- a/submodules/Camera/Sources/Camera.swift +++ b/submodules/Camera/Sources/Camera.swift @@ -57,7 +57,7 @@ final class CameraDeviceContext { self.output = CameraOutput(exclusive: exclusive) } - func configure(position: Camera.Position, previewView: CameraSimplePreviewView?, audio: Bool, photo: Bool, metadata: Bool) { + func configure(position: Camera.Position, previewView: CameraSimplePreviewView?, audio: Bool, photo: Bool, metadata: Bool, preferWide: Bool = false, preferLowerFramerate: Bool = false) { guard let session = self.session else { return } @@ -65,7 +65,7 @@ final class CameraDeviceContext { self.previewView = previewView self.device.configure(for: session, position: position, dual: !exclusive || additional) - self.device.configureDeviceFormat(maxDimensions: self.preferredMaxDimensions, maxFramerate: self.preferredMaxFrameRate) + self.device.configureDeviceFormat(maxDimensions: self.maxDimensions(additional: self.additional, preferWide: preferWide), maxFramerate: self.preferredMaxFrameRate(useLower: preferLowerFramerate)) self.input.configure(for: session, device: self.device, audio: audio) self.output.configure(for: session, device: self.device, input: self.input, previewView: previewView, audio: audio, photo: photo, metadata: metadata) @@ -82,18 +82,21 @@ final class CameraDeviceContext { self.input.invalidate(for: session) } - private var preferredMaxDimensions: CMVideoDimensions { - if self.additional { + private func maxDimensions(additional: Bool, preferWide: Bool) -> CMVideoDimensions { + if additional || preferWide { return CMVideoDimensions(width: 1920, height: 1440) } else { return CMVideoDimensions(width: 1920, height: 1080) } } - private var preferredMaxFrameRate: Double { + private func preferredMaxFrameRate(useLower: Bool) -> Double { if !self.exclusive { return 30.0 } + if useLower { + return 30.0 + } switch DeviceModel.current { case .iPhone15ProMax, .iPhone14ProMax, .iPhone13ProMax: return 60.0 @@ -117,6 +120,7 @@ private final class CameraContext { private var invalidated = false private let detectedCodesPipe = ValuePipe<[CameraCode]>() + private let audioLevelPipe = ValuePipe() fileprivate let modeChangePromise = ValuePromise(.none) var previewView: CameraPreviewView? @@ -256,7 +260,7 @@ private final class CameraContext { self._positionPromise.set(targetPosition) self.modeChange = .position - mainDeviceContext.configure(position: targetPosition, previewView: self.simplePreviewView, audio: self.initialConfiguration.audio, photo: self.initialConfiguration.photo, metadata: self.initialConfiguration.metadata) + mainDeviceContext.configure(position: targetPosition, previewView: self.simplePreviewView, audio: self.initialConfiguration.audio, photo: self.initialConfiguration.photo, metadata: self.initialConfiguration.metadata, preferWide: self.initialConfiguration.preferWide, preferLowerFramerate: self.initialConfiguration.preferLowerFramerate) self.queue.after(0.5) { self.modeChange = .none @@ -281,6 +285,10 @@ private final class CameraContext { } } + private var micLevelPeak: Int16 = 0 + private var micLevelPeakCount = 0 + + private var isDualCameraEnabled: Bool? public func setDualCameraEnabled(_ enabled: Bool, change: Bool = true) { guard enabled != self.isDualCameraEnabled else { @@ -336,7 +344,7 @@ private final class CameraContext { self.additionalDeviceContext = nil self.mainDeviceContext = CameraDeviceContext(session: self.session, exclusive: true, additional: false) - self.mainDeviceContext?.configure(position: self.positionValue, previewView: self.simplePreviewView, audio: self.initialConfiguration.audio, photo: self.initialConfiguration.photo, metadata: self.initialConfiguration.metadata) + self.mainDeviceContext?.configure(position: self.positionValue, previewView: self.simplePreviewView, audio: self.initialConfiguration.audio, photo: self.initialConfiguration.photo, metadata: self.initialConfiguration.metadata, preferWide: self.initialConfiguration.preferWide, preferLowerFramerate: self.initialConfiguration.preferLowerFramerate) } self.mainDeviceContext?.output.processSampleBuffer = { [weak self] sampleBuffer, pixelBuffer, connection in guard let self, let mainDeviceContext = self.mainDeviceContext else { @@ -352,6 +360,48 @@ private final class CameraContext { self.lastSnapshotTimestamp = timestamp } } + if self.initialConfiguration.reportAudioLevel { + self.mainDeviceContext?.output.processAudioBuffer = { [weak self] sampleBuffer in + guard let self else { + return + } + var blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer) + let numSamplesInBuffer = CMSampleBufferGetNumSamples(sampleBuffer) + var audioBufferList = AudioBufferList() + + CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer, bufferListSizeNeededOut: nil, bufferListOut: &audioBufferList, bufferListSize: MemoryLayout.size, blockBufferAllocator: nil, blockBufferMemoryAllocator: nil, flags: kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, blockBufferOut: &blockBuffer) + +// for bufferCount in 0.., count: Int) { + for i in 0..= 1200 { + let level = Float(self.micLevelPeak) / 4000.0 + self.audioLevelPipe.putNext(level) + + self.micLevelPeak = 0 + self.micLevelPeakCount = 0 + } + } + } + } + } self.mainDeviceContext?.output.processCodes = { [weak self] codes in self?.detectedCodesPipe.putNext(codes) } @@ -481,25 +531,40 @@ private final class CameraContext { additionalDeviceContext.output.stopRecording() ) |> mapToSignal { main, additional in if case let .finished(mainResult, _, duration, positionChangeTimestamps, _) = main, case let .finished(additionalResult, _, _, _, _) = additional { - var additionalTransitionImage = additionalResult.1 - if let cgImage = additionalResult.1.cgImage { - additionalTransitionImage = UIImage(cgImage: cgImage, scale: 1.0, orientation: .leftMirrored) + var additionalThumbnailImage = additionalResult.thumbnail + if let cgImage = additionalResult.thumbnail.cgImage { + additionalThumbnailImage = UIImage(cgImage: cgImage, scale: 1.0, orientation: .leftMirrored) } - return .single(.finished(mainResult, (additionalResult.0, additionalTransitionImage, true, additionalResult.3), duration, positionChangeTimestamps, CACurrentMediaTime())) + + return .single( + .finished( + main: mainResult, + additional: VideoCaptureResult.Result(path: additionalResult.path, thumbnail: additionalThumbnailImage, isMirrored: true, dimensions: additionalResult.dimensions), + duration: duration, + positionChangeTimestamps: positionChangeTimestamps, + captureTimestamp: CACurrentMediaTime() + ) + ) } else { return .complete() } } } else { - let mirror = self.positionValue == .front + let isMirrored = self.positionValue == .front return mainDeviceContext.output.stopRecording() |> map { result -> VideoCaptureResult in - if case let .finished(mainResult, _, duration, positionChangeTimestamps, time) = result { - var transitionImage = mainResult.1 - if mirror, let cgImage = transitionImage.cgImage { - transitionImage = UIImage(cgImage: cgImage, scale: 1.0, orientation: .leftMirrored) + if case let .finished(mainResult, _, duration, positionChangeTimestamps, captureTimestamp) = result { + var thumbnailImage = mainResult.thumbnail + if isMirrored, let cgImage = thumbnailImage.cgImage { + thumbnailImage = UIImage(cgImage: cgImage, scale: 1.0, orientation: .leftMirrored) } - return .finished((mainResult.0, transitionImage, mirror, mainResult.3), nil, duration, positionChangeTimestamps, time) + return .finished( + main: VideoCaptureResult.Result(path: mainResult.path, thumbnail: thumbnailImage, isMirrored: isMirrored, dimensions: mainResult.dimensions), + additional: nil, + duration: duration, + positionChangeTimestamps: positionChangeTimestamps, + captureTimestamp: captureTimestamp + ) } else { return result } @@ -511,6 +576,10 @@ private final class CameraContext { return self.detectedCodesPipe.signal() } + var audioLevel: Signal { + return self.audioLevelPipe.signal() + } + @objc private func sessionInterruptionEnded(notification: NSNotification) { } @@ -547,16 +616,20 @@ public final class Camera { let audio: Bool let photo: Bool let metadata: Bool - let preferredFps: Double + let preferWide: Bool + let preferLowerFramerate: Bool + let reportAudioLevel: Bool - public init(preset: Preset, position: Position, isDualEnabled: Bool = false, audio: Bool, photo: Bool, metadata: Bool, preferredFps: Double) { + public init(preset: Preset, position: Position, isDualEnabled: Bool = false, audio: Bool, photo: Bool, metadata: Bool, preferWide: Bool = false, preferLowerFramerate: Bool = false, reportAudioLevel: Bool = false) { self.preset = preset self.position = position self.isDualEnabled = isDualEnabled self.audio = audio self.photo = photo self.metadata = metadata - self.preferredFps = preferredFps + self.preferWide = preferWide + self.preferLowerFramerate = preferLowerFramerate + self.reportAudioLevel = reportAudioLevel } } @@ -567,7 +640,7 @@ public final class Camera { public let metrics: Camera.Metrics - public init(configuration: Camera.Configuration = Configuration(preset: .hd1920x1080, position: .back, audio: true, photo: false, metadata: false, preferredFps: 60.0), previewView: CameraSimplePreviewView? = nil, secondaryPreviewView: CameraSimplePreviewView? = nil) { + public init(configuration: Camera.Configuration = Configuration(preset: .hd1920x1080, position: .back, audio: true, photo: false, metadata: false), previewView: CameraSimplePreviewView? = nil, secondaryPreviewView: CameraSimplePreviewView? = nil) { Logger.shared.log("Camera", "Init") self.metrics = Camera.Metrics(model: DeviceModel.current) @@ -848,6 +921,20 @@ public final class Camera { } } + public var audioLevel: Signal { + return Signal { subscriber in + let disposable = MetaDisposable() + self.queue.async { + if let context = self.contextRef?.takeUnretainedValue() { + disposable.set(context.audioLevel.start(next: { codes in + subscriber.putNext(codes) + })) + } + } + return disposable + } + } + public enum ModeChange: Equatable { case none case position diff --git a/submodules/Camera/Sources/CameraOutput.swift b/submodules/Camera/Sources/CameraOutput.swift index e312b335f7a..09e977dfc57 100644 --- a/submodules/Camera/Sources/CameraOutput.swift +++ b/submodules/Camera/Sources/CameraOutput.swift @@ -8,7 +8,14 @@ import VideoToolbox import TelegramCore public enum VideoCaptureResult: Equatable { - case finished((String, UIImage, Bool, CGSize), (String, UIImage, Bool, CGSize)?, Double, [(Bool, Double)], Double) + public struct Result { + public let path: String + public let thumbnail: UIImage + public let isMirrored: Bool + public let dimensions: CGSize + } + + case finished(main: Result, additional: Result?, duration: Double, positionChangeTimestamps: [(Bool, Double)], captureTimestamp: Double) case failed public static func == (lhs: VideoCaptureResult, rhs: VideoCaptureResult) -> Bool { @@ -19,8 +26,8 @@ public enum VideoCaptureResult: Equatable { } else { return false } - case let .finished(_, _, lhsDuration, lhsChangeTimestamps, lhsTime): - if case let .finished(_, _, rhsDuration, rhsChangeTimestamps, rhsTime) = rhs, lhsDuration == rhsDuration, lhsTime == rhsTime { + case let .finished(_, _, lhsDuration, lhsChangeTimestamps, lhsTimestamp): + if case let .finished(_, _, rhsDuration, rhsChangeTimestamps, rhsTimestamp) = rhs, lhsDuration == rhsDuration, lhsTimestamp == rhsTimestamp { if lhsChangeTimestamps.count != rhsChangeTimestamps.count { return false } @@ -89,6 +96,7 @@ final class CameraOutput: NSObject { private var videoRecorder: VideoRecorder? var processSampleBuffer: ((CMSampleBuffer, CVImageBuffer, AVCaptureConnection) -> Void)? + var processAudioBuffer: ((CMSampleBuffer) -> Void)? var processCodes: (([CameraCode]) -> Void)? init(exclusive: Bool) { @@ -302,10 +310,26 @@ final class CameraOutput: NSObject { let outputFileURL = URL(fileURLWithPath: outputFilePath) let videoRecorder = VideoRecorder(configuration: VideoRecorder.Configuration(videoSettings: videoSettings, audioSettings: audioSettings), orientation: orientation, fileUrl: outputFileURL, completion: { [weak self] result in + guard let self else { + return + } if case let .success(transitionImage, duration, positionChangeTimestamps) = result { - self?.recordingCompletionPipe.putNext(.finished((outputFilePath, transitionImage ?? UIImage(), false, dimensions), nil, duration, positionChangeTimestamps.map { ($0 == .front, $1) }, CACurrentMediaTime())) + self.recordingCompletionPipe.putNext( + .finished( + main: VideoCaptureResult.Result( + path: outputFilePath, + thumbnail: transitionImage ?? UIImage(), + isMirrored: false, + dimensions: dimensions + ), + additional: nil, + duration: duration, + positionChangeTimestamps: positionChangeTimestamps.map { ($0 == .front, $1) }, + captureTimestamp: CACurrentMediaTime() + ) + ) } else { - self?.recordingCompletionPipe.putNext(.failed) + self.recordingCompletionPipe.putNext(.failed) } }) @@ -356,6 +380,8 @@ extension CameraOutput: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureA if let videoPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) { self.processSampleBuffer?(sampleBuffer, videoPixelBuffer, connection) + } else { +// self.processAudioBuffer?(sampleBuffer) } if let videoRecorder = self.videoRecorder, videoRecorder.isRecording { diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 36e9575c2dc..b30c2f8b33d 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1068,79 +1068,99 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } self.chatListDisplayNode.mainContainerNode.peerSelected = { [weak self] peer, threadId, animated, activateInput, promoInfo in - if let strongSelf = self { - if let navigationController = strongSelf.navigationController as? NavigationController { - var scrollToEndIfExists = false - if let layout = strongSelf.validLayout, case .regular = layout.metrics.widthClass { - scrollToEndIfExists = true + guard let self else { + return + } + let _ = (self.context.account.postbox.combinedView(keys: [.cachedPeerData(peerId: peer.id)]) + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] combinedView 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 scrollToEndIfExists = false + if let layout = self.validLayout, case .regular = layout.metrics.widthClass { + scrollToEndIfExists = true + } + + var openAsInlineForum = true + if let cachedData = cachedDataView.cachedPeerData as? CachedChannelData, case let .known(viewForumAsMessages) = cachedData.viewForumAsMessages, viewForumAsMessages { + openAsInlineForum = false + } + + if openAsInlineForum, case let .channel(channel) = peer, channel.flags.contains(.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.flags.contains(.isForum), let threadId { + let _ = self.context.sharedContext.navigateToForumThread(context: self.context, peerId: peer.id, threadId: threadId, messageId: nil, navigationController: navigationController, activateInput: nil, keepStack: .never).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 + } } - if case let .channel(channel) = peer, channel.flags.contains(.isForum), threadId == nil { - strongSelf.chatListDisplayNode.clearHighlightAnimated(true) - - if strongSelf.chatListDisplayNode.inlineStackContainerNode?.location == .forum(peerId: channel.id) { - strongSelf.setInlineChatList(location: nil) - } else { - strongSelf.setInlineChatList(location: .forum(peerId: channel.id)) + 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 } - } else { - if case let .channel(channel) = peer, channel.flags.contains(.isForum), let threadId { - let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: peer.id, threadId: threadId, messageId: nil, navigationController: navigationController, activateInput: nil, keepStack: .never).startStandalone() - strongSelf.chatListDisplayNode.clearHighlightAnimated(true) - } else { - var navigationAnimationOptions: NavigationAnimationOptions = [] - var groupId: EngineChatList.Group = .root - if case let .chatList(groupIdValue) = strongSelf.location { - groupId = groupIdValue - if case .root = groupIdValue { - navigationAnimationOptions = .removeOnMasterDetails - } - } - - let chatLocation: NavigateToChatControllerParams.Location - chatLocation = .peer(peer) - - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: chatLocation, activateInput: (activateInput && !peer.isDeleted) ? .text : nil, scrollToEndIfExists: scrollToEndIfExists, animated: !scrollToEndIfExists, options: navigationAnimationOptions, parentGroupId: groupId._asGroup(), chatListFilter: strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter?.id, completion: { [weak self] controller in - self?.chatListDisplayNode.mainContainerNode.currentItemNode.clearHighlightAnimated(true) - if let promoInfo = promoInfo { - switch promoInfo { - case .proxy: - let _ = (ApplicationSpecificNotice.getProxyAdsAcknowledgment(accountManager: strongSelf.context.sharedContext.accountManager) - |> deliverOnMainQueue).startStandalone(next: { value in - guard let strongSelf = self else { - return - } - if !value { - controller.displayPromoAnnouncement(text: strongSelf.presentationData.strings.DialogList_AdNoticeAlert) - let _ = ApplicationSpecificNotice.setProxyAdsAcknowledgment(accountManager: strongSelf.context.sharedContext.accountManager).startStandalone() - } - }) - case let .psa(type, _): - let _ = (ApplicationSpecificNotice.getPsaAcknowledgment(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peer.id) - |> deliverOnMainQueue).startStandalone(next: { value in - guard let strongSelf = self else { - return - } - if !value { - var text = strongSelf.presentationData.strings.ChatList_GenericPsaAlert - let key = "ChatList.PsaAlert.\(type)" - if let string = strongSelf.presentationData.strings.primaryComponent.dict[key] { - text = string - } else if let string = strongSelf.presentationData.strings.secondaryComponent?.dict[key] { - text = string - } - - controller.displayPromoAnnouncement(text: text) - let _ = ApplicationSpecificNotice.setPsaAcknowledgment(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peer.id).startStandalone() - } - }) + 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 @@ -3362,7 +3382,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return true } - public func setInlineChatList(location: ChatListControllerLocation?) { + public func setInlineChatList(location: ChatListControllerLocation?, animated: Bool = true) { if let location { let inlineNode = self.chatListDisplayNode.makeInlineChatList(location: location) let pendingSecondaryContext = ChatListLocationContext( @@ -3389,7 +3409,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } self.secondaryContext = pendingSecondaryContext - self.setToolbar(pendingSecondaryContext.toolbar, transition: .animated(duration: 0.5, curve: .spring)) + self.setToolbar(pendingSecondaryContext.toolbar, transition: animated ? .animated(duration: 0.5, curve: .spring) : .immediate) self.chatListDisplayNode.setInlineChatList(inlineStackContainerNode: inlineNode) self.updateNavigationMetadata() }) @@ -3399,7 +3419,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } self.secondaryContext = nil - self.setToolbar(self.primaryContext?.toolbar, transition: .animated(duration: 0.5, curve: .spring)) + self.setToolbar(self.primaryContext?.toolbar, transition: animated ? .animated(duration: 0.5, curve: .spring) : .immediate) self.chatListDisplayNode.setInlineChatList(inlineStackContainerNode: nil) self.updateNavigationMetadata() } @@ -3453,6 +3473,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let chatController = context.sharedContext.makeChatListController(context: context, location: .forum(peerId: peerId), controlsHistoryPreload: false, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: false) navigationController.replaceController(sourceController, with: chatController, animated: false) } + + let _ = context.engine.peers.updateForumViewAsMessages(peerId: peerId, value: false).startStandalone() }))) items.append(.action(ContextMenuActionItem(text: strings.Chat_ContextViewAsMessages, icon: { theme in if isViewingAsTopics { @@ -3471,8 +3493,21 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if let sourceController = sourceController as? ChatListControllerImpl, case .forum(peerId) = sourceController.location { navigationController.replaceController(sourceController, with: chatController, animated: false) } else { - navigationController.pushViewController(chatController) + let _ = (chatController.ready.get() + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { [weak sourceController] _ in + guard let sourceController = sourceController as? ChatListControllerImpl else { + return + } + if sourceController.chatListDisplayNode.inlineStackContainerNode?.location == .forum(peerId: peerId) { + sourceController.setInlineChatList(location: nil, animated: false) + } + }) + navigationController.pushViewController(chatController, animated: false) } + + let _ = context.engine.peers.updateForumViewAsMessages(peerId: peerId, value: true).startStandalone() }))) items.append(.separator) diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 0fd674db126..7e9ce3c6e93 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -211,7 +211,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo present(c, a) }, dismissInput: { self?.dismissInput() - }, contentContext: nil, progress: nil) + }, contentContext: nil, progress: nil, completion: nil) }) }, clearRecentSearch: { [weak self] in guard let strongSelf = self else { diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index e81ee30de1a..ef04a62b29e 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -211,9 +211,9 @@ private enum ChatListRecentEntry: Comparable, Identifiable { if peer.unreadCount > 0 { badge = ContactsPeerItemBadge(count: peer.unreadCount, type: isMuted ? .inactive : .active) } - + return ContactsPeerItem( - presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder), + presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, @@ -3531,7 +3531,7 @@ public final class ChatListSearchShimmerNode: ASDisplayNode { let chatListPresentationData = ChatListPresentationData(theme: presentationData.theme, fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true) - let peer1: EnginePeer = .user(TelegramUser(id: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "FirstName", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil)) + let peer1: EnginePeer = .user(TelegramUser(id: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "FirstName", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let timestamp1: Int32 = 100000 var peers: [EnginePeer.Id: EnginePeer] = [:] peers[peer1.id] = peer1 diff --git a/submodules/ChatListUI/Sources/ChatListShimmerNode.swift b/submodules/ChatListUI/Sources/ChatListShimmerNode.swift index 14526c30c65..5db4d9194e1 100644 --- a/submodules/ChatListUI/Sources/ChatListShimmerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListShimmerNode.swift @@ -150,7 +150,7 @@ final class ChatListShimmerNode: ASDisplayNode { let chatListPresentationData = ChatListPresentationData(theme: presentationData.theme, fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true) - let peer1: EnginePeer = .user(TelegramUser(id: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "FirstName", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil)) + let peer1: EnginePeer = .user(TelegramUser(id: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "FirstName", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let timestamp1: Int32 = 100000 let peers: [EnginePeer.Id: EnginePeer] = [:] let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 321be198a3d..6a28cb116f2 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -364,7 +364,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL } } return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListAdditionalCategoryItem( - presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder), + presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat), context: context, title: title, image: image, @@ -581,7 +581,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL } return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem( - presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder), + presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat), sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, context: context, @@ -620,7 +620,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL let status: ContactsPeerItemStatus = .none return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem( - presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder), + presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat), sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, context: context, @@ -682,7 +682,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL let peerContent: ContactsPeerItemPeer = .peer(peer: contactEntry.peer, chatPeer: contactEntry.peer) return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem( - presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder), + presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat), sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, context: context, @@ -904,7 +904,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL } return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem( - presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder), + presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat), sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, context: context, @@ -943,7 +943,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL let status: ContactsPeerItemStatus = .none return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem( - presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder), + presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat), sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, context: context, @@ -1005,7 +1005,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL let peerContent: ContactsPeerItemPeer = .peer(peer: contactEntry.peer, chatPeer: contactEntry.peer) return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem( - presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder), + presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat), sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, context: context, @@ -1072,7 +1072,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL } } return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListAdditionalCategoryItem( - presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder), + presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat), context: context, title: title, image: image, diff --git a/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift b/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift index 2b1855e0375..b39e98822ed 100644 --- a/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift +++ b/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift @@ -286,11 +286,6 @@ public struct ChatPresentationImportState: Equatable { } } -public enum ChatHistoryNodeHistoryState: Equatable { - case loading - case loaded(isEmpty: Bool) -} - public struct ChatPresentationTranslationState: Equatable { public var isEnabled: Bool public var fromLang: String @@ -418,6 +413,7 @@ public final class ChatPresentationInterfaceState: Equatable { public let voiceMessagesAvailable: Bool public let customEmojiAvailable: Bool public let threadData: ThreadData? + public let forumTopicData: ThreadData? public let isGeneralThreadClosed: Bool? public let translationState: ChatPresentationTranslationState? public let replyMessage: Message? @@ -491,13 +487,14 @@ public final class ChatPresentationInterfaceState: Equatable { self.voiceMessagesAvailable = true self.customEmojiAvailable = true self.threadData = threadData + self.forumTopicData = nil self.isGeneralThreadClosed = isGeneralThreadClosed self.translationState = nil self.replyMessage = replyMessage self.accountPeerColor = accountPeerColor } - public init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, explicitelyCanPinMessages: Bool, contactStatus: ChatContactStatus?, hasBots: Bool, isArchived: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, recordedMediaPreview: ChatRecordedMediaPreview?, inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult], inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, pinnedMessage: ChatPinnedMessage?, peerIsBlocked: Bool, peerIsMuted: Bool, peerDiscussionId: PeerId?, peerGeoLocation: PeerGeoLocation?, callsAvailable: Bool, callsPrivate: Bool, slowmodeState: ChatSlowmodeState?, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: UrlPreview?, editingUrlPreview: UrlPreview?, search: ChatSearchData?, searchQuerySuggestionResult: ChatPresentationInputQueryResult?, presentationReady: Bool, chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, accountPeerId: PeerId, mode: ChatControllerPresentationMode, hasScheduledMessages: Bool, autoremoveTimeout: Int32?, subject: ChatControllerSubject?, peerNearbyData: ChatPeerNearbyData?, greetingData: ChatGreetingData?, pendingUnpinnedAllMessages: Bool, activeGroupCallInfo: ChatActiveGroupCallInfo?, hasActiveGroupCall: Bool, importState: ChatPresentationImportState?, reportReason: ReportReason?, showCommands: Bool, hasBotCommands: Bool, showSendAsPeers: Bool, sendAsPeers: [SendAsPeer]?, botMenuButton: BotMenuButton, showWebView: Bool, currentSendAsPeerId: PeerId?, copyProtectionEnabled: Bool, hasPlentyOfMessages: Bool, isPremium: Bool, premiumGiftOptions: [CachedPremiumGiftOption], suggestPremiumGift: Bool, forceInputCommandsHidden: Bool, voiceMessagesAvailable: Bool, customEmojiAvailable: Bool, threadData: ThreadData?, isGeneralThreadClosed: Bool?, translationState: ChatPresentationTranslationState?, replyMessage: Message?, accountPeerColor: AccountPeerColor?) { + public init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, explicitelyCanPinMessages: Bool, contactStatus: ChatContactStatus?, hasBots: Bool, isArchived: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, recordedMediaPreview: ChatRecordedMediaPreview?, inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult], inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, pinnedMessage: ChatPinnedMessage?, peerIsBlocked: Bool, peerIsMuted: Bool, peerDiscussionId: PeerId?, peerGeoLocation: PeerGeoLocation?, callsAvailable: Bool, callsPrivate: Bool, slowmodeState: ChatSlowmodeState?, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: UrlPreview?, editingUrlPreview: UrlPreview?, search: ChatSearchData?, searchQuerySuggestionResult: ChatPresentationInputQueryResult?, presentationReady: Bool, chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, accountPeerId: PeerId, mode: ChatControllerPresentationMode, hasScheduledMessages: Bool, autoremoveTimeout: Int32?, subject: ChatControllerSubject?, peerNearbyData: ChatPeerNearbyData?, greetingData: ChatGreetingData?, pendingUnpinnedAllMessages: Bool, activeGroupCallInfo: ChatActiveGroupCallInfo?, hasActiveGroupCall: Bool, importState: ChatPresentationImportState?, reportReason: ReportReason?, showCommands: Bool, hasBotCommands: Bool, showSendAsPeers: Bool, sendAsPeers: [SendAsPeer]?, botMenuButton: BotMenuButton, showWebView: Bool, currentSendAsPeerId: PeerId?, copyProtectionEnabled: Bool, hasPlentyOfMessages: Bool, isPremium: Bool, premiumGiftOptions: [CachedPremiumGiftOption], suggestPremiumGift: Bool, forceInputCommandsHidden: Bool, voiceMessagesAvailable: Bool, customEmojiAvailable: Bool, threadData: ThreadData?, forumTopicData: ThreadData?, isGeneralThreadClosed: Bool?, translationState: ChatPresentationTranslationState?, replyMessage: Message?, accountPeerColor: AccountPeerColor?) { self.interfaceState = interfaceState self.chatLocation = chatLocation self.renderedPeer = renderedPeer @@ -565,6 +562,7 @@ public final class ChatPresentationInterfaceState: Equatable { self.voiceMessagesAvailable = voiceMessagesAvailable self.customEmojiAvailable = customEmojiAvailable self.threadData = threadData + self.forumTopicData = forumTopicData self.isGeneralThreadClosed = isGeneralThreadClosed self.translationState = translationState self.replyMessage = replyMessage @@ -779,6 +777,9 @@ public final class ChatPresentationInterfaceState: Equatable { if lhs.threadData != rhs.threadData { return false } + if lhs.forumTopicData != rhs.forumTopicData { + return false + } if lhs.isGeneralThreadClosed != rhs.isGeneralThreadClosed { return false } @@ -795,31 +796,31 @@ public final class ChatPresentationInterfaceState: Equatable { } public func updatedInterfaceState(_ f: (ChatInterfaceState) -> ChatInterfaceState) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: f(self.interfaceState), chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: f(self.interfaceState), chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedPeer(_ f: (RenderedPeer?) -> RenderedPeer?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: f(self.renderedPeer), isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: f(self.renderedPeer), isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedIsNotAccessible(_ isNotAccessible: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedExplicitelyCanPinMessages(_ explicitelyCanPinMessages: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedContactStatus(_ contactStatus: ChatContactStatus?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedHasBots(_ hasBots: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedIsArchived(_ isArchived: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedInputQueryResult(queryKind: ChatPresentationInputQueryKind, _ f: (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?) -> ChatPresentationInterfaceState { @@ -831,227 +832,231 @@ public final class ChatPresentationInterfaceState: Equatable { inputQueryResults.removeValue(forKey: queryKind) } - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedInputTextPanelState(_ f: (ChatTextInputPanelState) -> ChatTextInputPanelState) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: f(self.inputTextPanelState), editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: f(self.inputTextPanelState), editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedEditMessageState(_ editMessageState: ChatEditInterfaceMessageState?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedRecordedMediaPreview(_ recordedMediaPreview: ChatRecordedMediaPreview?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedInputMode(_ f: (ChatInputMode) -> ChatInputMode) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: f(self.inputMode), titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: f(self.inputMode), titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedTitlePanelContext(_ f: ([ChatTitlePanelContext]) -> [ChatTitlePanelContext]) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: f(self.titlePanelContexts), keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: f(self.titlePanelContexts), keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedKeyboardButtonsMessage(_ message: Message?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: message, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: message, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedPinnedMessage(_ pinnedMessage: ChatPinnedMessage?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedPeerIsBlocked(_ peerIsBlocked: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedPeerIsMuted(_ peerIsMuted: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedPeerDiscussionId(_ peerDiscussionId: PeerId?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedPeerGeoLocation(_ peerGeoLocation: PeerGeoLocation?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedCallsAvailable(_ callsAvailable: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedCallsPrivate(_ callsPrivate: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedSlowmodeState(_ slowmodeState: ChatSlowmodeState?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedBotStartPayload(_ botStartPayload: String?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedChatHistoryState(_ chatHistoryState: ChatHistoryNodeHistoryState?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedUrlPreview(_ urlPreview: UrlPreview?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedEditingUrlPreview(_ editingUrlPreview: UrlPreview?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedSearch(_ search: ChatSearchData?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedSearchQuerySuggestionResult(_ f: (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: f(self.searchQuerySuggestionResult), presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: f(self.searchQuerySuggestionResult), presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedMode(_ mode: ChatControllerPresentationMode) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedPresentationReady(_ presentationReady: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedTheme(_ theme: PresentationTheme) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedStrings(_ strings: PresentationStrings) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedDateTimeFormat(_ dateTimeFormat: PresentationDateTimeFormat) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedChatWallpaper(_ chatWallpaper: TelegramWallpaper) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedBubbleCorners(_ bubbleCorners: PresentationChatBubbleCorners) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedAutoremoveTimeout(_ autoremoveTimeout: Int32?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedPendingUnpinnedAllMessages(_ pendingUnpinnedAllMessages: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedActiveGroupCallInfo(_ activeGroupCallInfo: ChatActiveGroupCallInfo?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedHasActiveGroupCall(_ hasActiveGroupCall: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedImportState(_ importState: ChatPresentationImportState?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedReportReason(_ reportReason: ReportReason?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedShowCommands(_ showCommands: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedHasBotCommands(_ hasBotCommands: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedShowSendAsPeers(_ showSendAsPeers: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedSendAsPeers(_ sendAsPeers: [SendAsPeer]?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedCurrentSendAsPeerId(_ currentSendAsPeerId: PeerId?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedBotMenuButton(_ botMenuButton: BotMenuButton) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedShowWebView(_ showWebView: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedCopyProtectionEnabled(_ copyProtectionEnabled: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedHasPlentyOfMessages(_ hasPlentyOfMessages: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedIsPremium(_ isPremium: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedPremiumGiftOptions(_ premiumGiftOptions: [CachedPremiumGiftOption]) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedSuggestPremiumGift(_ suggestPremiumGift: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedForceInputCommandsHidden(_ forceInputCommandsHidden: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedVoiceMessagesAvailable(_ voiceMessagesAvailable: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedCustomEmojiAvailable(_ customEmojiAvailable: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedThreadData(_ threadData: ThreadData?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + } + + public func updatedForumTopicData(_ forumTopicData: ThreadData?) -> ChatPresentationInterfaceState { + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedIsGeneralThreadClosed(_ isGeneralThreadClosed: Bool?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedTranslationState(_ translationState: ChatPresentationTranslationState?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedReplyMessage(_ replyMessage: Message?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: replyMessage, accountPeerColor: self.accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: replyMessage, accountPeerColor: self.accountPeerColor) } public func updatedAccountPeerColor(_ accountPeerColor: AccountPeerColor?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: accountPeerColor) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: accountPeerColor) } } diff --git a/submodules/ComponentFlow/Source/Base/Transition.swift b/submodules/ComponentFlow/Source/Base/Transition.swift index c38f619a334..973ec228535 100644 --- a/submodules/ComponentFlow/Source/Base/Transition.swift +++ b/submodules/ComponentFlow/Source/Base/Transition.swift @@ -476,6 +476,10 @@ public struct Transition { self.setScale(layer: view.layer, scale: scale, delay: delay, completion: completion) } + public func setScaleWithSpring(view: UIView, scale: CGFloat, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) { + self.setScaleWithSpring(layer: view.layer, scale: scale, delay: delay, completion: completion) + } + public func setScale(layer: CALayer, scale: CGFloat, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) { let t = layer.presentation()?.transform ?? layer.transform let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13)) @@ -511,6 +515,40 @@ public struct Transition { } } + public func setScaleWithSpring(layer: CALayer, scale: CGFloat, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) { + let t = layer.presentation()?.transform ?? layer.transform + let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13)) + if currentScale == scale { + if let animation = layer.animation(forKey: "transform.scale") as? CABasicAnimation, let toValue = animation.toValue as? NSNumber { + if toValue.doubleValue == scale { + completion?(true) + return + } + } else { + completion?(true) + return + } + } + switch self.animation { + case .none: + layer.transform = CATransform3DMakeScale(scale, scale, 1.0) + completion?(true) + case let .curve(duration, _): + let previousScale = currentScale + layer.transform = CATransform3DMakeScale(scale, scale, 1.0) + layer.animateSpring( + from: previousScale as NSNumber, + to: scale as NSNumber, + keyPath: "transform.scale", + duration: duration, + delay: delay, + removeOnCompletion: true, + additive: false, + completion: completion + ) + } + } + public func setTransform(view: UIView, transform: CATransform3D, completion: ((Bool) -> Void)? = nil) { self.setTransform(layer: view.layer, transform: transform, completion: completion) } diff --git a/submodules/ComponentFlow/Source/Components/Button.swift b/submodules/ComponentFlow/Source/Components/Button.swift index 3a494c1e95b..41211e34ba8 100644 --- a/submodules/ComponentFlow/Source/Components/Button.swift +++ b/submodules/ComponentFlow/Source/Components/Button.swift @@ -7,6 +7,7 @@ public final class Button: Component { public let tag: AnyObject? public let automaticHighlight: Bool public let isEnabled: Bool + public let isExclusive: Bool public let action: () -> Void public let holdAction: (() -> Void)? public let highlightedAction: ActionSlot? @@ -36,6 +37,7 @@ public final class Button: Component { tag: AnyObject? = nil, automaticHighlight: Bool = true, isEnabled: Bool = true, + isExclusive: Bool = true, action: @escaping () -> Void, holdAction: (() -> Void)?, highlightedAction: ActionSlot? @@ -45,6 +47,7 @@ public final class Button: Component { self.tag = tag self.automaticHighlight = automaticHighlight self.isEnabled = isEnabled + self.isExclusive = isExclusive self.action = action self.holdAction = holdAction self.highlightedAction = highlightedAction @@ -57,12 +60,28 @@ public final class Button: Component { tag: self.tag, automaticHighlight: self.automaticHighlight, isEnabled: self.isEnabled, + isExclusive: self.isExclusive, action: self.action, holdAction: self.holdAction, highlightedAction: self.highlightedAction ) } + public func withIsExclusive(_ isExclusive: Bool) -> Button { + return Button( + content: self.content, + minSize: self.minSize, + tag: self.tag, + automaticHighlight: self.automaticHighlight, + isEnabled: self.isEnabled, + isExclusive: isExclusive, + action: self.action, + holdAction: self.holdAction, + highlightedAction: self.highlightedAction + ) + } + + public func withHoldAction(_ holdAction: (() -> Void)?) -> Button { return Button( content: self.content, @@ -70,6 +89,7 @@ public final class Button: Component { tag: self.tag, automaticHighlight: self.automaticHighlight, isEnabled: self.isEnabled, + isExclusive: self.isExclusive, action: self.action, holdAction: holdAction, highlightedAction: self.highlightedAction @@ -83,6 +103,7 @@ public final class Button: Component { tag: tag, automaticHighlight: self.automaticHighlight, isEnabled: self.isEnabled, + isExclusive: self.isExclusive, action: self.action, holdAction: self.holdAction, highlightedAction: self.highlightedAction @@ -105,6 +126,9 @@ public final class Button: Component { if lhs.isEnabled != rhs.isEnabled { return false } + if lhs.isExclusive != rhs.isExclusive { + return false + } return true } @@ -157,8 +181,6 @@ public final class Button: Component { super.init(frame: frame) - self.isExclusiveTouch = true - self.addSubview(self.contentView) self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) @@ -267,6 +289,7 @@ public final class Button: Component { self.updateAlpha(transition: transition) self.isEnabled = component.isEnabled + self.isExclusiveTouch = component.isExclusive transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(x: floor((size.width - contentSize.width) / 2.0), y: floor((size.height - contentSize.height) / 2.0)), size: contentSize), completion: nil) diff --git a/submodules/ComponentFlow/Source/Components/VStack.swift b/submodules/ComponentFlow/Source/Components/VStack.swift new file mode 100644 index 00000000000..bf54722e389 --- /dev/null +++ b/submodules/ComponentFlow/Source/Components/VStack.swift @@ -0,0 +1,60 @@ +import Foundation +import UIKit + +public final class VStack: CombinedComponent { + public typealias EnvironmentType = ChildEnvironment + + private let items: [AnyComponentWithIdentity] + private let spacing: CGFloat + + public init(_ items: [AnyComponentWithIdentity], spacing: CGFloat) { + self.items = items + self.spacing = spacing + } + + public static func ==(lhs: VStack, rhs: VStack) -> Bool { + if lhs.items != rhs.items { + return false + } + if lhs.spacing != rhs.spacing { + return false + } + return true + } + + public static var body: Body { + let children = ChildMap(environment: ChildEnvironment.self, keyedBy: AnyHashable.self) + + return { context in + let updatedChildren = context.component.items.map { item in + return children[item.id].update( + component: item.component, environment: { + context.environment[ChildEnvironment.self] + }, + availableSize: context.availableSize, + transition: context.transition + ) + } + + var size = CGSize(width: 0.0, height: 0.0) + for child in updatedChildren { + size.height += child.size.height + size.width = max(size.width, child.size.width) + } + size.height += context.component.spacing * CGFloat(updatedChildren.count - 1) + + var nextY = 0.0 + for child in updatedChildren { + context.add(child + .position(child.size.centered(in: CGRect(origin: CGPoint(x: floor((size.width - child.size.width) * 0.5), y: nextY), size: child.size)).center) + .appear(.default(scale: true, alpha: true)) + .disappear(.default(scale: true, alpha: true)) + ) + nextY += child.size.height + nextY += context.component.spacing + } + + return size + } + } +} diff --git a/submodules/Components/PagerComponent/Sources/PagerComponent.swift b/submodules/Components/PagerComponent/Sources/PagerComponent.swift index c0330219e4b..8abc3a1f936 100644 --- a/submodules/Components/PagerComponent/Sources/PagerComponent.swift +++ b/submodules/Components/PagerComponent/Sources/PagerComponent.swift @@ -747,6 +747,8 @@ public final class PagerComponent deliverOnMainQueue).start(next: { [weak self, weak controller] peer in if let strongSelf = self, let controller = controller { - controller.present(strongSelf.context.sharedContext.makeChatQrCodeScreen(context: strongSelf.context, peer: peer, threadId: nil), in: .window(.root)) + controller.present(strongSelf.context.sharedContext.makeChatQrCodeScreen(context: strongSelf.context, peer: peer, threadId: nil, temporary: false), in: .window(.root)) } }) } diff --git a/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift b/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift index 579c98cf807..3786a03d23f 100644 --- a/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift @@ -56,7 +56,7 @@ private enum InviteContactsEntry: Comparable, Identifiable { } else { status = .none } - let peer: EnginePeer = .user(TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: contact.firstName, lastName: contact.lastName, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil)) + let peer: EnginePeer = .user(TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: contact.firstName, lastName: contact.lastName, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .peer, peer: .peer(peer: peer, chatPeer: peer), status: status, enabled: true, selection: selection, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: ChatListSearchItemHeader(type: .contacts, theme: theme, strings: strings, actionTitle: nil, action: nil), action: { _ in interaction.toggleContact(id) }) diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index 96f7c72a878..13679a82fd2 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -2246,6 +2246,7 @@ public final class ContextController: ViewController, StandalonePresentableContr public var reactionItems: [ReactionContextItem] public var selectedReactionItems: Set public var animationCache: AnimationCache? + public var alwaysAllowPremiumReactions: Bool public var getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)? public var disablePositionLock: Bool public var tip: Tip? @@ -2259,6 +2260,7 @@ public final class ContextController: ViewController, StandalonePresentableContr reactionItems: [ReactionContextItem] = [], selectedReactionItems: Set = Set(), animationCache: AnimationCache? = nil, + alwaysAllowPremiumReactions: Bool = false, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)? = nil, disablePositionLock: Bool = false, tip: Tip? = nil, @@ -2271,6 +2273,7 @@ public final class ContextController: ViewController, StandalonePresentableContr self.animationCache = animationCache self.reactionItems = reactionItems self.selectedReactionItems = selectedReactionItems + self.alwaysAllowPremiumReactions = alwaysAllowPremiumReactions self.getEmojiContent = getEmojiContent self.disablePositionLock = disablePositionLock self.tip = tip @@ -2284,6 +2287,7 @@ public final class ContextController: ViewController, StandalonePresentableContr self.context = nil self.reactionItems = [] self.selectedReactionItems = Set() + self.alwaysAllowPremiumReactions = false self.getEmojiContent = nil self.disablePositionLock = false self.tip = nil diff --git a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift index 6609265629b..afef8cb144e 100644 --- a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift @@ -46,7 +46,7 @@ public protocol ContextControllerActionsStackItem: AnyObject { var id: AnyHashable? { get } var tip: ContextController.Tip? { get } var tipSignal: Signal? { get } - var reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, animationCache: AnimationCache, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)? { get } + var reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)? { get } var dismissed: (() -> Void)? { get } } @@ -905,7 +905,7 @@ final class ContextControllerActionsListStackItem: ContextControllerActionsStack let id: AnyHashable? let items: [ContextMenuItem] - let reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, animationCache: AnimationCache, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)? + let reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)? let tip: ContextController.Tip? let tipSignal: Signal? let dismissed: (() -> Void)? @@ -913,7 +913,7 @@ final class ContextControllerActionsListStackItem: ContextControllerActionsStack init( id: AnyHashable?, items: [ContextMenuItem], - reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, animationCache: AnimationCache, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)?, + reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)?, tip: ContextController.Tip?, tipSignal: Signal?, dismissed: (() -> Void)? @@ -1003,7 +1003,7 @@ final class ContextControllerActionsCustomStackItem: ContextControllerActionsSta let id: AnyHashable? private let content: ContextControllerItemsContent - let reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, animationCache: AnimationCache, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)? + let reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)? let tip: ContextController.Tip? let tipSignal: Signal? let dismissed: (() -> Void)? @@ -1011,7 +1011,7 @@ final class ContextControllerActionsCustomStackItem: ContextControllerActionsSta init( id: AnyHashable?, content: ContextControllerItemsContent, - reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, animationCache: AnimationCache, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)?, + reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)?, tip: ContextController.Tip?, tipSignal: Signal?, dismissed: (() -> Void)? @@ -1040,9 +1040,9 @@ final class ContextControllerActionsCustomStackItem: ContextControllerActionsSta } func makeContextControllerActionsStackItem(items: ContextController.Items) -> [ContextControllerActionsStackItem] { - var reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, animationCache: AnimationCache, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)? + var reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)? if let context = items.context, let animationCache = items.animationCache, !items.reactionItems.isEmpty { - reactionItems = (context, items.reactionItems, items.selectedReactionItems, animationCache, items.getEmojiContent) + reactionItems = (context, items.reactionItems, items.selectedReactionItems, animationCache, alwaysAllowPremiumReactions: items.alwaysAllowPremiumReactions, items.getEmojiContent) } switch items.content { case let .list(listItems): @@ -1166,7 +1166,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode { var tip: ContextController.Tip? let tipSignal: Signal? var tipNode: InnerTextSelectionTipContainerNode? - let reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, animationCache: AnimationCache, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)? + let reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)? let itemDismissed: (() -> Void)? var storedScrollingState: CGFloat? let positionLock: CGFloat? @@ -1181,7 +1181,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode { item: ContextControllerActionsStackItem, tip: ContextController.Tip?, tipSignal: Signal?, - reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, animationCache: AnimationCache, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)?, + reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)?, itemDismissed: (() -> Void)?, positionLock: CGFloat? ) { @@ -1332,7 +1332,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode { private var selectionPanGesture: UIPanGestureRecognizer? - var topReactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, animationCache: AnimationCache, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)? { + var topReactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)? { return self.itemContainers.last?.reactionItems } diff --git a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift index 430f27c9447..53af3875c4a 100644 --- a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift @@ -640,6 +640,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo presentationData: presentationData, items: reactionItems.reactionItems, selectedItems: reactionItems.selectedReactionItems, + alwaysAllowPremiumReactions: reactionItems.alwaysAllowPremiumReactions, getEmojiContent: reactionItems.getEmojiContent, isExpandedUpdated: { [weak self] transition in guard let strongSelf = self else { @@ -917,7 +918,11 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo reactionContextNode.updateLayout(size: layout.size, insets: UIEdgeInsets(top: topInset, left: layout.safeInsets.left, bottom: 0.0, right: layout.safeInsets.right), anchorRect: reactionAnchorRect, isCoveredByInput: isCoveredByInput, isAnimatingOut: isAnimatingOut, transition: reactionContextNodeTransition) - self.proposedReactionsPositionLock = contentRect.minY - 18.0 - reactionContextNode.contentHeight - (46.0 + 54.0 - 4.0) + if reactionContextNode.alwaysAllowPremiumReactions { + self.proposedReactionsPositionLock = contentRect.minY - 18.0 - reactionContextNode.contentHeight + } else { + self.proposedReactionsPositionLock = contentRect.minY - 18.0 - reactionContextNode.contentHeight - (46.0 + 54.0 - 4.0) + } } else { self.proposedReactionsPositionLock = nil } diff --git a/submodules/ContextUI/Sources/ContextSourceContainer.swift b/submodules/ContextUI/Sources/ContextSourceContainer.swift index a12b0cd5016..dc620965b84 100644 --- a/submodules/ContextUI/Sources/ContextSourceContainer.swift +++ b/submodules/ContextUI/Sources/ContextSourceContainer.swift @@ -610,6 +610,7 @@ final class ContextSourceContainer: ASDisplayNode { selection: presentationData.theme.contextMenu.primaryColor.withMultipliedAlpha(0.1) ), customLayout: TabSelectorComponent.CustomLayout( + font: Font.medium(14.0), spacing: 9.0 ), items: mappedItems, diff --git a/submodules/DebugSettingsUI/Sources/DebugController.swift b/submodules/DebugSettingsUI/Sources/DebugController.swift index 0263fb0cda3..f8474bb3757 100644 --- a/submodules/DebugSettingsUI/Sources/DebugController.swift +++ b/submodules/DebugSettingsUI/Sources/DebugController.swift @@ -54,6 +54,7 @@ private enum DebugControllerSection: Int32 { case sticker case logs case logging + case web case experiments case translation case videoExperiments @@ -82,6 +83,8 @@ private enum DebugControllerEntry: ItemListNodeEntry { case keepChatNavigationStack(PresentationTheme, Bool) case skipReadHistory(PresentationTheme, Bool) case unidirectionalSwipeToReply(Bool) + case dustEffect(Bool) + case callUIV2(Bool) case crashOnSlowQueries(PresentationTheme, Bool) case crashOnMemoryPressure(PresentationTheme, Bool) case clearTips(PresentationTheme) @@ -95,6 +98,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { case resetCacheIndex case reindexCache case resetBiometricsData(PresentationTheme) + case webViewInspection(Bool) case resetWebViewCache(PresentationTheme) case optimizeDatabase(PresentationTheme) case photoPreview(PresentationTheme, Bool) @@ -135,9 +139,11 @@ private enum DebugControllerEntry: ItemListNodeEntry { return DebugControllerSection.logs.rawValue case .logToFile, .logToConsole, .redactSensitiveData: return DebugControllerSection.logging.rawValue - case .keepChatNavigationStack, .skipReadHistory, .unidirectionalSwipeToReply, .crashOnSlowQueries, .crashOnMemoryPressure: + case .webViewInspection, .resetWebViewCache: + return DebugControllerSection.web.rawValue + case .keepChatNavigationStack, .skipReadHistory, .unidirectionalSwipeToReply, .dustEffect, .callUIV2, .crashOnSlowQueries, .crashOnMemoryPressure: return DebugControllerSection.experiments.rawValue - case .clearTips, .resetNotifications, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .resetWebViewCache, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .storiesExperiment, .storiesJpegExperiment, .playlistPlayback, .enableQuickReactionSwitch, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .inlineForums, .localTranscription, .enableReactionOverrides, .restorePurchases: + case .clearTips, .resetNotifications, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .storiesExperiment, .storiesJpegExperiment, .playlistPlayback, .enableQuickReactionSwitch, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .inlineForums, .localTranscription, .enableReactionOverrides, .restorePurchases: return DebugControllerSection.experiments.rawValue case .logTranslationRecognition, .resetTranslationStates: return DebugControllerSection.translation.rawValue @@ -184,76 +190,82 @@ private enum DebugControllerEntry: ItemListNodeEntry { return 11 case .redactSensitiveData: return 12 - case .keepChatNavigationStack: + case .webViewInspection: + return 13 + case .resetWebViewCache: return 14 - case .skipReadHistory: + case .keepChatNavigationStack: return 15 - case .unidirectionalSwipeToReply: + case .skipReadHistory: return 16 - case .crashOnSlowQueries: + case .unidirectionalSwipeToReply: return 17 - case .crashOnMemoryPressure: + case .dustEffect: return 18 - case .clearTips: + case .callUIV2: return 19 - case .resetNotifications: + case .crashOnSlowQueries: return 20 - case .crash: + case .crashOnMemoryPressure: return 21 - case .resetData: + case .clearTips: return 22 - case .resetDatabase: + case .resetNotifications: return 23 - case .resetDatabaseAndCache: + case .crash: return 24 - case .resetHoles: + case .resetData: return 25 - case .reindexUnread: + case .resetDatabase: return 26 - case .resetCacheIndex: + case .resetDatabaseAndCache: return 27 - case .reindexCache: + case .resetHoles: return 28 - case .resetBiometricsData: + case .reindexUnread: return 29 - case .resetWebViewCache: + case .resetCacheIndex: return 30 - case .optimizeDatabase: + case .reindexCache: return 31 - case .photoPreview: + case .resetBiometricsData: return 32 - case .knockoutWallpaper: + case .optimizeDatabase: return 33 - case .experimentalCompatibility: + case .photoPreview: return 34 - case .enableDebugDataDisplay: + case .knockoutWallpaper: return 35 - case .acceleratedStickers: + case .experimentalCompatibility: return 36 - case .inlineForums: + case .enableDebugDataDisplay: return 37 - case .localTranscription: + case .acceleratedStickers: return 38 - case .enableReactionOverrides: + case .inlineForums: return 39 - case .restorePurchases: + case .localTranscription: return 40 - case .logTranslationRecognition: + case .enableReactionOverrides: return 41 - case .resetTranslationStates: + case .restorePurchases: return 42 - case .storiesExperiment: + case .logTranslationRecognition: return 43 - case .storiesJpegExperiment: + case .resetTranslationStates: return 44 - case .playlistPlayback: + case .storiesExperiment: return 45 - case .enableQuickReactionSwitch: + case .storiesJpegExperiment: return 46 - case .voiceConference: + case .playlistPlayback: return 47 + case .enableQuickReactionSwitch: + return 48 + case .voiceConference: + return 49 case let .preferredVideoCodec(index, _, _, _): - return 48 + index + return 50 + index case .disableVideoAspectScaling: return 100 case .enableNetworkFramework: @@ -294,7 +306,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { } else { UIPasteboard.general.setData(data, forPasteboardType: dataType) } - context.sharedContext.openResolvedUrl(.importStickers, context: context, urlContext: .generic, navigationController: arguments.getNavigationController(), forceExternal: false, openPeer: { _, _ in }, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { c, a in arguments.presentController(c, a as? ViewControllerPresentationArguments) }, dismissInput: {}, contentContext: nil, progress: nil) + context.sharedContext.openResolvedUrl(.importStickers, context: context, urlContext: .generic, navigationController: arguments.getNavigationController(), forceExternal: false, openPeer: { _, _ in }, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { c, a in arguments.presentController(c, a as? ViewControllerPresentationArguments) }, dismissInput: {}, contentContext: nil, progress: nil, completion: nil) } }) case .sendLogs: @@ -994,6 +1006,22 @@ private enum DebugControllerEntry: ItemListNodeEntry { return settings }).start() }) + case let .dustEffect(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Dust Effect", value: value, sectionId: self.section, style: .blocks, updated: { value in + let _ = updateExperimentalUISettingsInteractively(accountManager: arguments.sharedContext.accountManager, { settings in + var settings = settings + settings.dustEffect = value + return settings + }).start() + }) + case let .callUIV2(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Call UI V2", value: value, sectionId: self.section, style: .blocks, updated: { value in + let _ = updateExperimentalUISettingsInteractively(accountManager: arguments.sharedContext.accountManager, { settings in + var settings = settings + settings.callUIV2 = value + return settings + }).start() + }) case let .crashOnSlowQueries(_, value): return ItemListSwitchItem(presentationData: presentationData, title: "Crash when slow", value: value, sectionId: self.section, style: .blocks, updated: { value in let _ = updateExperimentalUISettingsInteractively(accountManager: arguments.sharedContext.accountManager, { settings in @@ -1196,6 +1224,14 @@ private enum DebugControllerEntry: ItemListNodeEntry { return settings.withUpdatedBiometricsDomainState(nil).withUpdatedShareBiometricsDomainState(nil) }).start() }) + case let .webViewInspection(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Allow Web View Inspection", value: value, sectionId: self.section, style: .blocks, updated: { value in + let _ = updateExperimentalUISettingsInteractively(accountManager: arguments.sharedContext.accountManager, { settings in + var settings = settings + settings.allowWebViewInspection = value + return settings + }).start() + }) case .resetWebViewCache: return ItemListActionItem(presentationData: presentationData, title: "Clear Web View Cache", kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: { WKWebsiteDataStore.default().removeData(ofTypes: [WKWebsiteDataTypeDiskCache, WKWebsiteDataTypeMemoryCache], modifiedSince: Date(timeIntervalSince1970: 0), completionHandler:{ }) @@ -1459,9 +1495,14 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present entries.append(.redactSensitiveData(presentationData.theme, loggingSettings.redactSensitiveData)) if isMainApp { + entries.append(.webViewInspection(experimentalSettings.allowWebViewInspection)) + entries.append(.resetWebViewCache(presentationData.theme)) + entries.append(.keepChatNavigationStack(presentationData.theme, experimentalSettings.keepChatNavigationStack)) entries.append(.skipReadHistory(presentationData.theme, experimentalSettings.skipReadHistory)) entries.append(.unidirectionalSwipeToReply(experimentalSettings.unidirectionalSwipeToReply)) + entries.append(.dustEffect(experimentalSettings.dustEffect)) + entries.append(.callUIV2(experimentalSettings.callUIV2)) } entries.append(.crashOnSlowQueries(presentationData.theme, experimentalSettings.crashOnLongQueries)) entries.append(.crashOnMemoryPressure(presentationData.theme, experimentalSettings.crashOnMemoryPressure)) @@ -1478,7 +1519,6 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present entries.append(.reindexUnread(presentationData.theme)) entries.append(.resetCacheIndex) entries.append(.reindexCache) - entries.append(.resetWebViewCache(presentationData.theme)) } entries.append(.optimizeDatabase(presentationData.theme)) if isMainApp { diff --git a/submodules/Display/Source/ContainedViewLayoutTransition.swift b/submodules/Display/Source/ContainedViewLayoutTransition.swift index 6dedb8bc910..cbdc55b4a27 100644 --- a/submodules/Display/Source/ContainedViewLayoutTransition.swift +++ b/submodules/Display/Source/ContainedViewLayoutTransition.swift @@ -110,6 +110,34 @@ public extension CGRect { } } +private extension CALayer { + func animate(from: AnyObject, to: AnyObject, keyPath: String, duration: Double, delay: Double, curve: ContainedViewLayoutTransitionCurve, removeOnCompletion: Bool, additive: Bool, completion: ((Bool) -> Void)? = nil) { + let timingFunction: String + let mediaTimingFunction: CAMediaTimingFunction? + switch curve { + case .spring: + timingFunction = kCAMediaTimingFunctionSpring + mediaTimingFunction = nil + default: + timingFunction = CAMediaTimingFunctionName.easeInEaseOut.rawValue + mediaTimingFunction = curve.mediaTimingFunction + } + + self.animate( + from: from, + to: to, + keyPath: keyPath, + timingFunction: timingFunction, + duration: duration, + delay: delay, + mediaTimingFunction: mediaTimingFunction, + removeOnCompletion: removeOnCompletion, + additive: additive, + completion: completion + ) + } +} + public extension ContainedViewLayoutTransition { func animation() -> CABasicAnimation? { switch self { @@ -1222,7 +1250,11 @@ public extension ContainedViewLayoutTransition { completion?(true) return } - let t = node.layer.sublayerTransform + self.updateSublayerTransformScaleAdditive(layer: node.layer, scale: scale, completion: completion) + } + + func updateSublayerTransformScaleAdditive(layer: CALayer, scale: CGFloat, completion: ((Bool) -> Void)? = nil) { + let t = layer.sublayerTransform let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13)) if currentScale.isEqual(to: scale) { if let completion = completion { @@ -1233,16 +1265,16 @@ public extension ContainedViewLayoutTransition { switch self { case .immediate: - node.layer.removeAnimation(forKey: "sublayerTransform") - node.layer.sublayerTransform = CATransform3DMakeScale(scale, scale, 1.0) + layer.removeAnimation(forKey: "sublayerTransform") + layer.sublayerTransform = CATransform3DMakeScale(scale, scale, 1.0) if let completion = completion { completion(true) } case let .animated(duration, curve): - let t = node.layer.sublayerTransform + let t = layer.sublayerTransform let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13)) - node.layer.sublayerTransform = CATransform3DMakeScale(scale, scale, 1.0) - node.layer.animate(from: -(scale - currentScale) as NSNumber, to: 0.0 as NSNumber, keyPath: "sublayerTransform.scale", timingFunction: curve.timingFunction, duration: duration, delay: 0.0, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: true, completion: { + layer.sublayerTransform = CATransform3DMakeScale(scale, scale, 1.0) + layer.animate(from: -(scale - currentScale) as NSNumber, to: 0.0 as NSNumber, keyPath: "sublayerTransform.scale", timingFunction: curve.timingFunction, duration: duration, delay: 0.0, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: true, completion: { result in if let completion = completion { completion(result) @@ -1540,6 +1572,25 @@ public extension ContainedViewLayoutTransition { }) } } + + func attachAnimation(view: UIView, id: String, completion: @escaping (Bool) -> Void) { + switch self { + case .immediate: + completion(true) + case let .animated(duration, curve): + view.layer.animate( + from: 0.0 as NSNumber, + to: 1.0 as NSNumber, + keyPath: id, + duration: duration, + delay: 0.0, + curve: curve, + removeOnCompletion: true, + additive: false, + completion: completion + ) + } + } } public struct CombinedTransition { diff --git a/submodules/Display/Source/ContextControllerSourceNode.swift b/submodules/Display/Source/ContextControllerSourceNode.swift index b01479bfe59..4dc6d55fdcb 100644 --- a/submodules/Display/Source/ContextControllerSourceNode.swift +++ b/submodules/Display/Source/ContextControllerSourceNode.swift @@ -153,129 +153,6 @@ open class ContextControllerSourceNode: ContextReferenceContentNode { } } -/*open class ContextControllerSourceNode: ASDisplayNode { - private var viewImpl: ContextControllerSourceView { - return self.view as! ContextControllerSourceView - } - - public var contextGesture: ContextGesture? { - if self.isNodeLoaded { - return self.viewImpl.contextGesture - } else { - return nil - } - } - - public var isGestureEnabled: Bool = true { - didSet { - if self.isNodeLoaded { - self.viewImpl.isGestureEnabled = self.isGestureEnabled - } - } - } - - public var beginDelay: Double = 0.12 { - didSet { - if self.isNodeLoaded { - self.viewImpl.beginDelay = self.beginDelay - } - } - } - - public var animateScale: Bool = true { - didSet { - if self.isNodeLoaded { - self.viewImpl.animateScale = self.animateScale - } - } - } - - public var activated: ((ContextGesture, CGPoint) -> Void)? { - didSet { - if self.isNodeLoaded { - self.viewImpl.activated = self.activated - } - } - } - - public var shouldBegin: ((CGPoint) -> Bool)? { - didSet { - if self.isNodeLoaded { - self.viewImpl.shouldBegin = self.shouldBegin - } - } - } - - public var customActivationProgress: ((CGFloat, ContextGestureTransition) -> Void)? { - didSet { - if self.isNodeLoaded { - self.viewImpl.customActivationProgress = self.customActivationProgress - } - } - } - - public weak var additionalActivationProgressLayer: CALayer? { - didSet { - if self.isNodeLoaded { - self.viewImpl.additionalActivationProgressLayer = self.additionalActivationProgressLayer - } - } - } - - public var targetNodeForActivationProgress: ASDisplayNode? { - didSet { - if self.isNodeLoaded { - self.viewImpl.targetNodeForActivationProgress = self.targetNodeForActivationProgress - } - } - } - - public var targetViewForActivationProgress: UIView? { - didSet { - if self.isNodeLoaded { - self.viewImpl.targetViewForActivationProgress = self.targetViewForActivationProgress - } - } - } - - public var targetNodeForActivationProgressContentRect: CGRect? { - didSet { - if self.isNodeLoaded { - self.viewImpl.targetNodeForActivationProgressContentRect = self.targetNodeForActivationProgressContentRect - } - } - } - - override public init() { - super.init() - - self.setViewBlock({ - return ContextControllerSourceView(frame: CGRect()) - }) - } - - override open func didLoad() { - super.didLoad() - - self.viewImpl.isGestureEnabled = self.isGestureEnabled - self.viewImpl.beginDelay = self.beginDelay - self.viewImpl.animateScale = self.animateScale - self.viewImpl.activated = self.activated - self.viewImpl.shouldBegin = self.shouldBegin - self.viewImpl.customActivationProgress = self.customActivationProgress - self.viewImpl.additionalActivationProgressLayer = self.additionalActivationProgressLayer - self.viewImpl.targetNodeForActivationProgress = self.targetNodeForActivationProgress - self.viewImpl.targetViewForActivationProgress = self.targetViewForActivationProgress - self.viewImpl.targetNodeForActivationProgressContentRect = self.targetNodeForActivationProgressContentRect - } - - public func cancelGesture() { - if self.isNodeLoaded { - self.viewImpl.cancelGesture() - } - } -}*/ - open class ContextControllerSourceView: UIView { public private(set) var contextGesture: ContextGesture? diff --git a/submodules/Display/Source/DisplayLinkAnimator.swift b/submodules/Display/Source/DisplayLinkAnimator.swift index 27623743414..8a3af9d8c52 100644 --- a/submodules/Display/Source/DisplayLinkAnimator.swift +++ b/submodules/Display/Source/DisplayLinkAnimator.swift @@ -8,28 +8,45 @@ public protocol SharedDisplayLinkDriverLink: AnyObject { } public final class SharedDisplayLinkDriver { + public enum FramesPerSecond: Comparable { + case fps(Int) + case max + + public static func <(lhs: FramesPerSecond, rhs: FramesPerSecond) -> Bool { + switch lhs { + case let .fps(lhsFps): + switch rhs { + case let .fps(rhsFps): + return lhsFps < rhsFps + case .max: + return true + } + case .max: + return false + } + } + } + public typealias Link = SharedDisplayLinkDriverLink public static let shared = SharedDisplayLinkDriver() - private let useNative: Bool - public final class LinkImpl: Link { private let driver: SharedDisplayLinkDriver - public let needsHighestFramerate: Bool - let update: () -> Void + public let framesPerSecond: FramesPerSecond + let update: (CGFloat) -> Void var isValid: Bool = true public var isPaused: Bool = false { didSet { if self.isPaused != oldValue { - driver.requestUpdate() + self.driver.requestUpdate() } } } - init(driver: SharedDisplayLinkDriver, needsHighestFramerate: Bool, update: @escaping () -> Void) { + init(driver: SharedDisplayLinkDriver, framesPerSecond: FramesPerSecond, update: @escaping (CGFloat) -> Void) { self.driver = driver - self.needsHighestFramerate = needsHighestFramerate + self.framesPerSecond = framesPerSecond self.update = update } @@ -38,65 +55,26 @@ public final class SharedDisplayLinkDriver { } } - public final class NativeLinkImpl: Link { - private var displayLink: CADisplayLink? - - public var isPaused: Bool = false { - didSet { - self.displayLink?.isPaused = self.isPaused - } - } - - init(needsHighestFramerate: Bool, update: @escaping () -> Void) { - let displayLink = CADisplayLink(target: DisplayLinkTarget { - update() - }, selector: #selector(DisplayLinkTarget.event)) - - if #available(iOS 15.0, *) { - let maxFps = Float(UIScreen.main.maximumFramesPerSecond) - if maxFps > 61.0 { - let frameRateRange: CAFrameRateRange - if needsHighestFramerate { - frameRateRange = CAFrameRateRange(minimum: 30.0, maximum: 120.0, preferred: 120.0) - } else { - frameRateRange = .default - } - if displayLink.preferredFrameRateRange != frameRateRange { - displayLink.preferredFrameRateRange = frameRateRange - } - } - } - - self.displayLink = displayLink - displayLink.add(to: .main, forMode: .common) - } - - deinit { - self.displayLink?.invalidate() - } - - public func invalidate() { - self.displayLink?.invalidate() - } - } - private final class RequestContext { weak var link: LinkImpl? + let framesPerSecond: FramesPerSecond - init(link: LinkImpl) { + var lastDuration: Double = 0.0 + + init(link: LinkImpl, framesPerSecond: FramesPerSecond) { self.link = link + self.framesPerSecond = framesPerSecond } } private var displayLink: CADisplayLink? - private var hasRequestedHighestFramerate: Bool = false private var requests: [RequestContext] = [] private var isInForeground: Bool = false + private var isProcessingEvent: Bool = false + private var isUpdateRequested: Bool = false private init() { - self.useNative = false - let _ = NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: nil, using: { [weak self] _ in guard let self else { return @@ -134,15 +112,21 @@ public final class SharedDisplayLinkDriver { } private func requestUpdate() { - self.update() + if self.isProcessingEvent { + self.isUpdateRequested = true + } else { + self.update() + } } private func update() { var hasActiveItems = false - var needHighestFramerate = false + var maxFramesPerSecond: FramesPerSecond = .fps(30) for request in self.requests { if let link = request.link { - needHighestFramerate = link.needsHighestFramerate + if link.framesPerSecond > maxFramesPerSecond { + maxFramesPerSecond = link.framesPerSecond + } if link.isValid && !link.isPaused { hasActiveItems = true break @@ -163,13 +147,19 @@ public final class SharedDisplayLinkDriver { let maxFps = Float(UIScreen.main.maximumFramesPerSecond) if maxFps > 61.0 { let frameRateRange: CAFrameRateRange - if needHighestFramerate { + switch maxFramesPerSecond { + case let .fps(fps): + if fps > 60 { + frameRateRange = CAFrameRateRange(minimum: 30.0, maximum: 120.0, preferred: 120.0) + } else { + frameRateRange = .default + } + case .max: frameRateRange = CAFrameRateRange(minimum: 30.0, maximum: 120.0, preferred: 120.0) - } else { - frameRateRange = .default } if displayLink.preferredFrameRateRange != frameRateRange { displayLink.preferredFrameRateRange = frameRateRange + print("SharedDisplayLinkDriver: switch to \(frameRateRange)") } } } @@ -182,12 +172,35 @@ public final class SharedDisplayLinkDriver { } } - @objc private func displayLinkEvent() { + @objc private func displayLinkEvent(displayLink: CADisplayLink) { + self.isProcessingEvent = true + + let duration = displayLink.targetTimestamp - displayLink.timestamp + var removeIndices: [Int]? - for i in 0 ..< self.requests.count { - if let link = self.requests[i].link, link.isValid { + loop: for i in 0 ..< self.requests.count { + let request = self.requests[i] + if let link = request.link, link.isValid { if !link.isPaused { - link.update() + var itemDuration = duration + + switch request.framesPerSecond { + case let .fps(value): + let secondsPerFrame = 1.0 / CGFloat(value) + itemDuration = secondsPerFrame + request.lastDuration += duration + if request.lastDuration >= secondsPerFrame * 0.95 { + //print("item \(link) accepting cycle: \(request.lastDuration - duration) + \(duration) = \(request.lastDuration) >= \(secondsPerFrame)") + } else { + //print("item \(link) skipping cycle: \(request.lastDuration - duration) + \(duration) < \(secondsPerFrame)") + continue loop + } + case .max: + break + } + + request.lastDuration = 0.0 + link.update(itemDuration) } } else { if removeIndices == nil { @@ -203,34 +216,36 @@ public final class SharedDisplayLinkDriver { } if self.requests.isEmpty { - self.update() + self.isUpdateRequested = true } } - } - - public func add(needsHighestFramerate: Bool = true, _ update: @escaping () -> Void) -> Link { - if self.useNative { - return NativeLinkImpl(needsHighestFramerate: needsHighestFramerate, update: update) - } else { - let link = LinkImpl(driver: self, needsHighestFramerate: needsHighestFramerate, update: update) - self.requests.append(RequestContext(link: link)) - + + self.isProcessingEvent = false + if self.isUpdateRequested { + self.isUpdateRequested = false self.update() - - return link } } + + public func add(framesPerSecond: FramesPerSecond = .fps(60), _ update: @escaping (CGFloat) -> Void) -> Link { + let link = LinkImpl(driver: self, framesPerSecond: framesPerSecond, update: update) + self.requests.append(RequestContext(link: link, framesPerSecond: framesPerSecond)) + + self.update() + + return link + } } public final class DisplayLinkTarget: NSObject { - private let f: () -> Void + private let f: (CADisplayLink) -> Void - public init(_ f: @escaping () -> Void) { + public init(_ f: @escaping (CADisplayLink) -> Void) { self.f = f } - @objc public func event() { - self.f() + @objc public func event(_ displayLink: CADisplayLink) { + self.f(displayLink) } } @@ -253,7 +268,7 @@ public final class DisplayLinkAnimator { self.startTime = CACurrentMediaTime() - self.displayLink = SharedDisplayLinkDriver.shared.add { [weak self] in + self.displayLink = SharedDisplayLinkDriver.shared.add { [weak self] _ in self?.tick() } self.displayLink?.isPaused = false @@ -308,7 +323,7 @@ public final class ConstantDisplayLinkAnimator { didSet { if self.isPaused != oldValue { if !self.isPaused && self.displayLink == nil { - let displayLink = SharedDisplayLinkDriver.shared.add { [weak self] in + let displayLink = SharedDisplayLinkDriver.shared.add { [weak self] _ in self?.tick() } self.displayLink = displayLink diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift index 54f60309962..73111b4f6d4 100644 --- a/submodules/Display/Source/ListView.swift +++ b/submodules/Display/Source/ListView.swift @@ -394,7 +394,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture didSet { if self.isAuxiliaryDisplayLinkEnabled { if self.auxiliaryDisplayLinkHandle == nil { - self.auxiliaryDisplayLinkHandle = SharedDisplayLinkDriver.shared.add(needsHighestFramerate: true, { [weak self] in + self.auxiliaryDisplayLinkHandle = SharedDisplayLinkDriver.shared.add(framesPerSecond: .max, { [weak self] _ in guard let self else { return } @@ -2435,17 +2435,26 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } if node.index == nil { + var duration = insertionAnimationDuration + var hasCustomRemoveAnimation = false + if let value = self.customItemDeleteAnimationDuration(itemNode: node) { + duration = value + hasCustomRemoveAnimation = true + } + if node.animationForKey("height") == nil || !(node is ListViewTempItemNode) { - node.addHeightAnimation(0.0, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp) + node.addHeightAnimation(0.0, duration: duration * UIView.animationDurationFactor(), beginAt: timestamp) } if node.animationForKey("apparentHeight") == nil || !(node is ListViewTempItemNode) { - node.addApparentHeightAnimation(0.0, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp, update: { [weak node] progress, currentValue in + node.addApparentHeightAnimation(0.0, duration: duration * UIView.animationDurationFactor(), beginAt: timestamp, update: { [weak node] progress, currentValue in if let node = node { node.animateFrameTransition(progress, currentValue) } }) } - node.animateRemoved(timestamp, duration: insertionAnimationDuration * UIView.animationDurationFactor()) + if !hasCustomRemoveAnimation { + node.animateRemoved(timestamp, duration: duration * UIView.animationDurationFactor()) + } } else if animated { if takenAnimation { if let previousFrame = previousFrame { @@ -4669,6 +4678,14 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } } } + + public func forEachRemovedItemNode(_ f: (ASDisplayNode) -> Void) { + for itemNode in self.itemNodes { + if itemNode.index == nil { + f(itemNode) + } + } + } public func enumerateItemNodes(_ f: (ASDisplayNode) -> Bool) { for itemNode in self.itemNodes { @@ -5033,6 +5050,10 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } return self.scrollWithDirection(scrollDirection, distance: distance) } + + open func customItemDeleteAnimationDuration(itemNode: ListViewItemNode) -> Double? { + return nil + } } private func findAccessibilityFocus(_ node: ASDisplayNode) -> Bool { diff --git a/submodules/Display/Source/Navigation/NavigationController.swift b/submodules/Display/Source/Navigation/NavigationController.swift index 6ad2308f96b..a50a4cbde32 100644 --- a/submodules/Display/Source/Navigation/NavigationController.swift +++ b/submodules/Display/Source/Navigation/NavigationController.swift @@ -364,7 +364,7 @@ open class NavigationController: UINavigationController, ContainableController, } } - private func updateContainers(layout rawLayout: ContainerViewLayout, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void = {}) { + private func updateContainers(layout rawLayout: ContainerViewLayout, transition: ContainedViewLayoutTransition, completion externalCompletion: (() -> Void)? = nil) { self.isUpdatingContainers = true var layout = rawLayout @@ -1268,6 +1268,16 @@ open class NavigationController: UINavigationController, ContainableController, if initialPrefersOnScreenNavigationHidden != updatedPrefersOnScreenNavigationHidden { self.currentWindow?.invalidatePrefersOnScreenNavigationHidden() } + + if let externalCompletion { + if transition.isAnimated { + transition.attachAnimation(view: self.view, id: "updateContainers", completion: { _ in + externalCompletion() + }) + } else { + externalCompletion() + } + } } private func controllerRemoved(_ controller: ViewController) { @@ -1346,8 +1356,9 @@ open class NavigationController: UINavigationController, ContainableController, } public func pushViewController(_ controller: ViewController, animated: Bool = true, completion: @escaping () -> Void) { - self.pushViewController(controller, animated: animated) - completion() + var controllers = self.viewControllers + controllers.append(controller) + self.setViewControllers(controllers, animated: animated, completion: completion) } open override func pushViewController(_ viewController: UIViewController, animated: Bool) { @@ -1468,6 +1479,10 @@ open class NavigationController: UINavigationController, ContainableController, } open override func setViewControllers(_ viewControllers: [UIViewController], animated: Bool) { + self.setViewControllers(viewControllers, animated: animated, completion: {}) + } + + public func setViewControllers(_ viewControllers: [UIViewController], animated: Bool, completion: @escaping () -> Void) { for i in 0 ..< viewControllers.count { guard let controller = viewControllers[i] as? ViewController else { continue @@ -1500,7 +1515,10 @@ open class NavigationController: UINavigationController, ContainableController, if let layout = self.validLayout { self.updateContainers(layout: layout, transition: animated ? .animated(duration: 0.5, curve: .spring) : .immediate, completion: { [weak self] in self?.notifyAccessibilityScreenChanged() + completion() }) + } else { + completion() } self._viewControllersPromise.set(self.viewControllers) } diff --git a/submodules/Display/Source/NavigationBar.swift b/submodules/Display/Source/NavigationBar.swift index 489396a9ff1..1ddb6767cf8 100644 --- a/submodules/Display/Source/NavigationBar.swift +++ b/submodules/Display/Source/NavigationBar.swift @@ -476,6 +476,8 @@ open class NavigationBar: ASDisplayNode { } } } + + public static let thinBackArrowImage = generateTintedImage(image: UIImage(bundleImageName: "Navigation/BackArrow"), color: .white)?.withRenderingMode(.alwaysTemplate) public static let titleFont = Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]) @@ -729,11 +731,20 @@ open class NavigationBar: ASDisplayNode { self.updateAccessibilityElements() } + public var enableAutomaticBackButton: Bool = true + var _previousItem: NavigationPreviousAction? public internal(set) var previousItem: NavigationPreviousAction? { get { + if !self.enableAutomaticBackButton { + return nil + } return self._previousItem } set(value) { + if !self.enableAutomaticBackButton { + self._previousItem = nil + return + } if self._previousItem != value { if let previousValue = self._previousItem, case let .item(itemValue) = previousValue { if let previousItemListenerKey = self.previousItemListenerKey { diff --git a/submodules/Display/Source/NavigationButtonNode.swift b/submodules/Display/Source/NavigationButtonNode.swift index 46a5ad84cc0..fe281a62114 100644 --- a/submodules/Display/Source/NavigationButtonNode.swift +++ b/submodules/Display/Source/NavigationButtonNode.swift @@ -338,6 +338,10 @@ public final class NavigationButtonNode: ContextControllerSourceNode { return nil } + public var mainContentNode: ASDisplayNode? { + return self.nodes.first + } + public var pressed: (Int) -> () = { _ in } public var highlightChanged: (Int, Bool) -> () = { _, _ in } @@ -397,6 +401,8 @@ public final class NavigationButtonNode: ContextControllerSourceNode { } } + public var contentsColor: UIColor? + public func updateManualAlpha(alpha: CGFloat, transition: ContainedViewLayoutTransition) { for node in self.nodes { transition.updateAlpha(node: node, alpha: alpha) @@ -411,6 +417,7 @@ public final class NavigationButtonNode: ContextControllerSourceNode { node = NavigationButtonItemNode() node.color = self.color node.rippleColor = self.rippleColor + node.layer.layerTintColor = self.contentsColor?.cgColor node.highlightChanged = { [weak node, weak self] value in if let strongSelf = self, let node = node { if let index = strongSelf.nodes.firstIndex(where: { $0 === node }) { @@ -454,6 +461,7 @@ public final class NavigationButtonNode: ContextControllerSourceNode { node = NavigationButtonItemNode() node.color = self.color node.rippleColor = self.rippleColor + node.layer.layerTintColor = self.contentsColor?.cgColor node.highlightChanged = { [weak node, weak self] value in if let strongSelf = self, let node = node { if let index = strongSelf.nodes.firstIndex(where: { $0 === node }) { diff --git a/submodules/Display/Source/Nodes/ASImageNode.swift b/submodules/Display/Source/Nodes/ASImageNode.swift index 2ebf0e99870..f4b8406aaa6 100644 --- a/submodules/Display/Source/Nodes/ASImageNode.swift +++ b/submodules/Display/Source/Nodes/ASImageNode.swift @@ -47,4 +47,8 @@ open class ASImageNode: ASDisplayNode { override public func calculateSizeThatFits(_ contrainedSize: CGSize) -> CGSize { return self.image?.size ?? CGSize() } + + public var asdf: Int { + return 1234 + } } diff --git a/submodules/Display/Source/PortalView.swift b/submodules/Display/Source/PortalView.swift index bf872b22fcd..7762252bce8 100644 --- a/submodules/Display/Source/PortalView.swift +++ b/submodules/Display/Source/PortalView.swift @@ -3,6 +3,7 @@ import UIKitRuntimeUtils public class PortalView { public let view: UIView & UIKitPortalViewProtocol + public weak var sourceView: UIView? public init?(matchPosition: Bool = true) { guard let view = makePortalView(matchPosition) else { @@ -13,6 +14,7 @@ public class PortalView { func reloadPortal(sourceView: PortalSourceView) { self.view.sourceView = sourceView + self.sourceView = sourceView if let portalSuperview = self.view.superview, let index = portalSuperview.subviews.firstIndex(of: self.view) { portalSuperview.insertSubview(self.view, at: index) diff --git a/submodules/Display/Source/TextNode.swift b/submodules/Display/Source/TextNode.swift index 6326867e41b..cabd9cb7291 100644 --- a/submodules/Display/Source/TextNode.swift +++ b/submodules/Display/Source/TextNode.swift @@ -1158,7 +1158,33 @@ private func addAttachment(attachment: UIImage, line: TextNodeLine, ascent: CGFl } open class TextNode: ASDisplayNode { + public struct RenderContentTypes: OptionSet { + public var rawValue: Int + + public init(rawValue: Int) { + self.rawValue = rawValue + } + + public static let text = RenderContentTypes(rawValue: 1 << 0) + public static let emoji = RenderContentTypes(rawValue: 1 << 1) + + public static let all: RenderContentTypes = [.text, .emoji] + } + + private final class DrawingParameters: NSObject { + let cachedLayout: TextNodeLayout? + let renderContentTypes: RenderContentTypes + + init(cachedLayout: TextNodeLayout?, renderContentTypes: RenderContentTypes) { + self.cachedLayout = cachedLayout + self.renderContentTypes = renderContentTypes + + super.init() + } + } + public internal(set) var cachedLayout: TextNodeLayout? + public var renderContentTypes: RenderContentTypes = .all override public init() { super.init() @@ -1606,17 +1632,15 @@ open class TextNode: ASDisplayNode { return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: alignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textShadowBlur: textShadowBlur, textStroke: textStroke, displaySpoilers: displaySpoilers) } - if maximumNumberOfLines == 0 { - var found = false - attributedString.enumerateAttribute(NSAttributedString.Key("Attribute__Blockquote"), in: NSRange(location: 0, length: attributedString.length), using: { value, effectiveRange, _ in - if let _ = value as? TextNodeBlockQuoteData { - found = true - } - }) - - if found { - return calculateLayoutV2(attributedString: attributedString, minimumNumberOfLines: minimumNumberOfLines, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, backgroundColor: backgroundColor, constrainedSize: constrainedSize, alignment: alignment, verticalAlignment: verticalAlignment, lineSpacingFactor: lineSpacingFactor, cutout: cutout, insets: insets, lineColor: lineColor, textShadowColor: textShadowColor, textShadowBlur: textShadowBlur, textStroke: textStroke, displaySpoilers: displaySpoilers, displayEmbeddedItemsUnderSpoilers: displayEmbeddedItemsUnderSpoilers, customTruncationToken: customTruncationToken) + var found = false + attributedString.enumerateAttribute(NSAttributedString.Key("Attribute__Blockquote"), in: NSRange(location: 0, length: attributedString.length), using: { value, effectiveRange, _ in + if let _ = value as? TextNodeBlockQuoteData { + found = true } + }) + + if found { + return calculateLayoutV2(attributedString: attributedString, minimumNumberOfLines: minimumNumberOfLines, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, backgroundColor: backgroundColor, constrainedSize: constrainedSize, alignment: alignment, verticalAlignment: verticalAlignment, lineSpacingFactor: lineSpacingFactor, cutout: cutout, insets: insets, lineColor: lineColor, textShadowColor: textShadowColor, textShadowBlur: textShadowBlur, textStroke: textStroke, displaySpoilers: displaySpoilers, displayEmbeddedItemsUnderSpoilers: displayEmbeddedItemsUnderSpoilers, customTruncationToken: customTruncationToken) } let stringLength = attributedString.length @@ -2171,7 +2195,7 @@ open class TextNode: ASDisplayNode { } override public func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? { - return self.cachedLayout + return DrawingParameters(cachedLayout: self.cachedLayout, renderContentTypes: self.renderContentTypes) } @objc override public class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) { @@ -2194,8 +2218,13 @@ open class TextNode: ASDisplayNode { var blendMode: CGBlendMode = .normal + var renderContentTypes: RenderContentTypes = .all + if let parameters = parameters as? DrawingParameters { + renderContentTypes = parameters.renderContentTypes + } + var clearRects: [CGRect] = [] - if let layout = parameters as? TextNodeLayout { + if let layout = (parameters as? DrawingParameters)?.cachedLayout { if !isRasterizing || layout.backgroundColor != nil { context.setBlendMode(.copy) blendMode = .copy @@ -2411,6 +2440,18 @@ open class TextNode: ASDisplayNode { continue } + if renderContentTypes != .all { + if let font = attributes["NSFont"] as? UIFont, font.fontName.contains("ColorEmoji") { + if !renderContentTypes.contains(.emoji) { + continue + } + } else { + if !renderContentTypes.contains(.text) { + continue + } + } + } + var fixDoubleEmoji = false if glyphCount == 2, let font = attributes["NSFont"] as? UIFont, font.fontName.contains("ColorEmoji"), let string = layout.attributedString { let range = CTRunGetStringRange(run) diff --git a/submodules/Display/Source/TooltipController.swift b/submodules/Display/Source/TooltipController.swift index 25605bcd661..3cd74e0f200 100644 --- a/submodules/Display/Source/TooltipController.swift +++ b/submodules/Display/Source/TooltipController.swift @@ -101,6 +101,7 @@ open class TooltipController: ViewController, StandalonePresentableController { public private(set) var content: TooltipControllerContent private let baseFontSize: CGFloat private let balancedTextLayout: Bool + private let isBlurred: Bool open func updateContent(_ content: TooltipControllerContent, animated: Bool, extendTimer: Bool, arrowOnBottom: Bool = true) { if self.content != content { @@ -131,10 +132,11 @@ open class TooltipController: ViewController, StandalonePresentableController { public var dismissed: ((Bool) -> Void)? - public init(content: TooltipControllerContent, baseFontSize: CGFloat, balancedTextLayout: Bool = false, timeout: Double = 2.0, dismissByTapOutside: Bool = false, dismissByTapOutsideSource: Bool = false, dismissImmediatelyOnLayoutUpdate: Bool = false, arrowOnBottom: Bool = true, padding: CGFloat = 8.0, innerPadding: UIEdgeInsets = UIEdgeInsets()) { + public init(content: TooltipControllerContent, baseFontSize: CGFloat, balancedTextLayout: Bool = false, isBlurred: Bool = false, timeout: Double = 2.0, dismissByTapOutside: Bool = false, dismissByTapOutsideSource: Bool = false, dismissImmediatelyOnLayoutUpdate: Bool = false, arrowOnBottom: Bool = true, padding: CGFloat = 8.0, innerPadding: UIEdgeInsets = UIEdgeInsets()) { self.content = content self.baseFontSize = baseFontSize self.balancedTextLayout = balancedTextLayout + self.isBlurred = isBlurred self.timeout = timeout self.dismissByTapOutside = dismissByTapOutside self.dismissByTapOutsideSource = dismissByTapOutsideSource @@ -157,7 +159,7 @@ open class TooltipController: ViewController, StandalonePresentableController { } override open func loadDisplayNode() { - self.displayNode = TooltipControllerNode(content: self.content, baseFontSize: self.baseFontSize, balancedTextLayout: self.balancedTextLayout, dismiss: { [weak self] tappedInside in + self.displayNode = TooltipControllerNode(content: self.content, baseFontSize: self.baseFontSize, balancedTextLayout: self.balancedTextLayout, isBlurred: self.isBlurred, dismiss: { [weak self] tappedInside in self?.dismiss(tappedInside: tappedInside) }, dismissByTapOutside: self.dismissByTapOutside, dismissByTapOutsideSource: self.dismissByTapOutsideSource) self.controllerNode.padding = self.padding diff --git a/submodules/Display/Source/TooltipControllerNode.swift b/submodules/Display/Source/TooltipControllerNode.swift index 8740ca13d09..b190f3a0fe0 100644 --- a/submodules/Display/Source/TooltipControllerNode.swift +++ b/submodules/Display/Source/TooltipControllerNode.swift @@ -26,15 +26,17 @@ final class TooltipControllerNode: ASDisplayNode { private var dismissedByTouchOutside = false private var dismissByTapOutsideSource = false - init(content: TooltipControllerContent, baseFontSize: CGFloat, balancedTextLayout: Bool, dismiss: @escaping (Bool) -> Void, dismissByTapOutside: Bool, dismissByTapOutsideSource: Bool) { + init(content: TooltipControllerContent, baseFontSize: CGFloat, balancedTextLayout: Bool, isBlurred: Bool, dismiss: @escaping (Bool) -> Void, dismissByTapOutside: Bool, dismissByTapOutsideSource: Bool) { self.baseFontSize = baseFontSize self.balancedTextLayout = balancedTextLayout self.dismissByTapOutside = dismissByTapOutside self.dismissByTapOutsideSource = dismissByTapOutsideSource - self.containerNode = ContextMenuContainerNode(isBlurred: false, isDark: true) - self.containerNode.containerNode.backgroundColor = UIColor(white: 0.0, alpha: 0.8) + self.containerNode = ContextMenuContainerNode(isBlurred: isBlurred, isDark: true) + if !isBlurred { + self.containerNode.containerNode.backgroundColor = UIColor(white: 0.0, alpha: 0.8) + } self.imageNode = ASImageNode() self.imageNode.image = content.image diff --git a/submodules/Display/Source/TransformImageNode.swift b/submodules/Display/Source/TransformImageNode.swift index 23e600c96df..4e06c3ab488 100644 --- a/submodules/Display/Source/TransformImageNode.swift +++ b/submodules/Display/Source/TransformImageNode.swift @@ -3,6 +3,7 @@ import UIKit import AsyncDisplayKit import SwiftSignalKit import AVFoundation +import UIKitRuntimeUtils public struct TransformImageNodeContentAnimations: OptionSet { public var rawValue: Int32 @@ -28,61 +29,21 @@ open class TransformImageNode: ASDisplayNode { private var overlayColor: UIColor? private var overlayNode: ASDisplayNode? - private var captureProtectedContentLayer: CaptureProtectedContentLayer? - public var captureProtected: Bool = false { didSet { if self.captureProtected != oldValue { - if self.captureProtected { - if self.captureProtectedContentLayer == nil { - let captureProtectedContentLayer = CaptureProtectedContentLayer() - self.captureProtectedContentLayer = captureProtectedContentLayer - if #available(iOS 13.0, *) { - captureProtectedContentLayer.preventsCapture = true - captureProtectedContentLayer.preventsDisplaySleepDuringVideoPlayback = false - } - captureProtectedContentLayer.frame = self.bounds - self.layer.addSublayer(captureProtectedContentLayer) - var hasImage = false - if let image = self.image { - hasImage = true - if let cmSampleBuffer = image.cmSampleBuffer { - captureProtectedContentLayer.enqueue(cmSampleBuffer) - } - } - if hasImage { - Queue.mainQueue().after(0.1) { - self.contents = nil - } - } else { - self.contents = nil - } - } - } else if let captureProtectedContentLayer = self.captureProtectedContentLayer { - self.captureProtectedContentLayer = nil - captureProtectedContentLayer.removeFromSuperlayer() - self.contents = self.image?.cgImage + if self.isNodeLoaded { + setLayerDisableScreenshots(self.layer, self.captureProtected) } } } } - open override var bounds: CGRect { - didSet { - if let captureProtectedContentLayer = self.captureProtectedContentLayer, super.bounds.size != oldValue.size { - captureProtectedContentLayer.frame = super.bounds - } - } - } - open override var frame: CGRect { didSet { if let overlayNode = self.overlayNode { overlayNode.frame = self.bounds } - if let captureProtectedContentLayer = self.captureProtectedContentLayer, super.bounds.size != oldValue.size { - captureProtectedContentLayer.frame = super.bounds - } } } @@ -96,6 +57,9 @@ open class TransformImageNode: ASDisplayNode { if #available(iOSApplicationExtension 11.0, iOS 11.0, *), !self.isLayerBacked { self.view.accessibilityIgnoresInvertColors = true } + if self.captureProtected { + setLayerDisableScreenshots(self.layer, self.captureProtected) + } } public func reset() { @@ -138,30 +102,24 @@ open class TransformImageNode: ASDisplayNode { strongSelf.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) } } else if strongSelf.contentAnimations.contains(.subsequentUpdates) { - if let _ = strongSelf.captureProtectedContentLayer { - } else { - let tempLayer = CALayer() - tempLayer.frame = strongSelf.bounds - tempLayer.contentsGravity = strongSelf.layer.contentsGravity - tempLayer.contents = strongSelf.contents - strongSelf.layer.addSublayer(tempLayer) - tempLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak tempLayer] _ in - tempLayer?.removeFromSuperlayer() - }) + let tempLayer = CALayer() + if strongSelf.captureProtected { + setLayerDisableScreenshots(tempLayer, strongSelf.captureProtected) } + tempLayer.frame = strongSelf.bounds + tempLayer.contentsGravity = strongSelf.layer.contentsGravity + tempLayer.contents = strongSelf.contents + strongSelf.layer.addSublayer(tempLayer) + tempLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak tempLayer] _ in + tempLayer?.removeFromSuperlayer() + }) } var imageUpdate: UIImage? if let (transform, arguments, image) = next { strongSelf.currentTransform = transform strongSelf.currentArguments = arguments - if let captureProtectedContentLayer = strongSelf.captureProtectedContentLayer { - if let cmSampleBuffer = image?.cmSampleBuffer { - captureProtectedContentLayer.enqueue(cmSampleBuffer) - } - } else { - strongSelf.contents = image?.cgImage - } + strongSelf.contents = image?.cgImage strongSelf.image = image imageUpdate = image } @@ -198,13 +156,7 @@ open class TransformImageNode: ASDisplayNode { return } if let image = updatedImage { - if let captureProtectedContentLayer = strongSelf.captureProtectedContentLayer { - if let cmSampleBuffer = image.cmSampleBuffer { - captureProtectedContentLayer.enqueue(cmSampleBuffer) - } - } else { - strongSelf.contents = image.cgImage - } + strongSelf.contents = image.cgImage strongSelf.image = image strongSelf.currentArguments = arguments if let _ = strongSelf.overlayColor { @@ -277,24 +229,6 @@ open class TransformImageNode: ASDisplayNode { } } -public class CaptureProtectedContentLayer: AVSampleBufferDisplayLayer { - override public func action(forKey event: String) -> CAAction? { - return nullAction - } - - override public init() { - super.init() - } - - override public init(layer: Any) { - super.init(layer: layer) - } - - required public init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - open class TransformImageView: UIView { public var imageUpdated: ((UIImage?) -> Void)? public var contentAnimations: TransformImageNodeContentAnimations = [] @@ -305,50 +239,21 @@ open class TransformImageView: UIView { private var argumentsPromise = ValuePromise(ignoreRepeated: true) public private(set) var image: UIImage? - private var captureProtectedContentLayer: CaptureProtectedContentLayer? - private var overlayColor: UIColor? private var overlayView: UIView? - - open override var bounds: CGRect { - didSet { - if let captureProtectedContentLayer = self.captureProtectedContentLayer, super.bounds.size != oldValue.size { - captureProtectedContentLayer.frame = super.bounds - } - } - } open override var frame: CGRect { didSet { if let overlayView = self.overlayView { overlayView.frame = self.bounds } - if let captureProtectedContentLayer = self.captureProtectedContentLayer, super.bounds.size != oldValue.size { - captureProtectedContentLayer.frame = super.bounds - } } } public var captureProtected: Bool = false { didSet { if self.captureProtected != oldValue { - if self.captureProtected { - if self.captureProtectedContentLayer == nil { - let captureProtectedContentLayer = CaptureProtectedContentLayer() - captureProtectedContentLayer.frame = self.bounds - self.layer.addSublayer(captureProtectedContentLayer) - if let image = self.image { - if let cmSampleBuffer = image.cmSampleBuffer { - captureProtectedContentLayer.enqueue(cmSampleBuffer) - } - } - self.layer.contents = nil - } - } else if let captureProtectedContentLayer = self.captureProtectedContentLayer { - self.captureProtectedContentLayer = nil - captureProtectedContentLayer.removeFromSuperlayer() - self.layer.contents = self.image?.cgImage - } + setLayerDisableScreenshots(self.layer, self.captureProtected) } } } @@ -375,7 +280,6 @@ open class TransformImageView: UIView { self.currentTransform = nil self.layer.contents = nil self.image = nil - self.captureProtectedContentLayer?.flushAndRemoveImage() } public func setSignal(_ signal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>, attemptSynchronously: Bool = false, dispatchOnDisplayLink: Bool = true) { @@ -410,30 +314,24 @@ open class TransformImageView: UIView { strongSelf.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) } } else if strongSelf.contentAnimations.contains(.subsequentUpdates) { - if let _ = strongSelf.captureProtectedContentLayer { - } else { - let tempLayer = CALayer() - tempLayer.frame = strongSelf.bounds - tempLayer.contentsGravity = strongSelf.layer.contentsGravity - tempLayer.contents = strongSelf.layer.contents - strongSelf.layer.addSublayer(tempLayer) - tempLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak tempLayer] _ in - tempLayer?.removeFromSuperlayer() - }) + let tempLayer = CALayer() + if strongSelf.captureProtected { + setLayerDisableScreenshots(tempLayer, strongSelf.captureProtected) } + tempLayer.frame = strongSelf.bounds + tempLayer.contentsGravity = strongSelf.layer.contentsGravity + tempLayer.contents = strongSelf.layer.contents + strongSelf.layer.addSublayer(tempLayer) + tempLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak tempLayer] _ in + tempLayer?.removeFromSuperlayer() + }) } var imageUpdate: UIImage? if let (transform, arguments, image) = next { strongSelf.currentTransform = transform strongSelf.currentArguments = arguments - if let captureProtectedContentLayer = strongSelf.captureProtectedContentLayer { - if let cmSampleBuffer = image?.cmSampleBuffer { - captureProtectedContentLayer.enqueue(cmSampleBuffer) - } - } else { - strongSelf.layer.contents = image?.cgImage - } + strongSelf.layer.contents = image?.cgImage strongSelf.image = image imageUpdate = image } diff --git a/submodules/Display/Source/UIKitUtils.swift b/submodules/Display/Source/UIKitUtils.swift index 6d68a705817..0ec54265fa8 100644 --- a/submodules/Display/Source/UIKitUtils.swift +++ b/submodules/Display/Source/UIKitUtils.swift @@ -172,6 +172,12 @@ public extension UIColor { } } + func contrastRatio(with other: UIColor) -> CGFloat { + let l1 = self.lightness + let l2 = other.lightness + return (max(l1, l2) + 0.05) / (min(l1, l2) + 0.05) + } + var brightness: CGFloat { var hue: CGFloat = 0.0 var saturation: CGFloat = 0.0 @@ -280,6 +286,35 @@ public extension UIColor { return self } + func blendOver(background: UIColor) -> UIColor { + let base = background + let blend = self + + func overlayChannel(baseChannel: CGFloat, blendChannel: CGFloat) -> CGFloat { + if baseChannel < 0.5 { + return 2 * baseChannel * blendChannel + } else { + return 1 - 2 * (1 - baseChannel) * (1 - blendChannel) + } + } + + var baseRed: CGFloat = 0, baseGreen: CGFloat = 0, baseBlue: CGFloat = 0, baseAlpha: CGFloat = 0 + base.getRed(&baseRed, green: &baseGreen, blue: &baseBlue, alpha: &baseAlpha) + + var blendRed: CGFloat = 0, blendGreen: CGFloat = 0, blendBlue: CGFloat = 0, blendAlpha: CGFloat = 0 + blend.getRed(&blendRed, green: &blendGreen, blue: &blendBlue, alpha: &blendAlpha) + + var red = overlayChannel(baseChannel: baseRed, blendChannel: blendRed) + var green = overlayChannel(baseChannel: baseGreen, blendChannel: blendGreen) + var blue = overlayChannel(baseChannel: baseBlue, blendChannel: blendBlue) + + red = max(0.0, min(1.0, red)) + green = max(0.0, min(1.0, green)) + blue = max(0.0, min(1.0, blue)) + + return UIColor(red: red, green: green, blue: blue, alpha: blendAlpha).blitOver(background, alpha: 1.0) + } + func withMultipliedAlpha(_ alpha: CGFloat) -> UIColor { var r1: CGFloat = 0.0 var g1: CGFloat = 0.0 diff --git a/submodules/Display/Source/ViewController.swift b/submodules/Display/Source/ViewController.swift index 8bce9989dd5..e49739fc004 100644 --- a/submodules/Display/Source/ViewController.swift +++ b/submodules/Display/Source/ViewController.swift @@ -155,7 +155,7 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject { return self.prefersOnScreenNavigationHidden } - public internal(set) var previousItem: NavigationPreviousAction? + open var previousItem: NavigationPreviousAction? open var navigationPresentation: ViewControllerNavigationPresentation = .default open var _presentedInModal: Bool = false diff --git a/submodules/DrawingUI/BUILD b/submodules/DrawingUI/BUILD index 700800721d8..988ae6f3fd9 100644 --- a/submodules/DrawingUI/BUILD +++ b/submodules/DrawingUI/BUILD @@ -104,6 +104,7 @@ swift_library( "//submodules/TelegramUI/Components/CameraButtonComponent", "//submodules/ReactionSelectionNode", "//submodules/TelegramUI/Components/EntityKeyboard", + "//submodules/Camera", ], visibility = [ "//visibility:public", diff --git a/submodules/DrawingUI/Sources/DrawingBubbleEntity.swift b/submodules/DrawingUI/Sources/DrawingBubbleEntityView.swift similarity index 100% rename from submodules/DrawingUI/Sources/DrawingBubbleEntity.swift rename to submodules/DrawingUI/Sources/DrawingBubbleEntityView.swift diff --git a/submodules/DrawingUI/Sources/DrawingEntitiesView.swift b/submodules/DrawingUI/Sources/DrawingEntitiesView.swift index 5c5d889f371..04b99612282 100644 --- a/submodules/DrawingUI/Sources/DrawingEntitiesView.swift +++ b/submodules/DrawingUI/Sources/DrawingEntitiesView.swift @@ -15,7 +15,11 @@ private func makeEntityView(context: AccountContext, entity: DrawingEntity) -> D } else if let entity = entity as? DrawingSimpleShapeEntity { return DrawingSimpleShapeEntityView(context: context, entity: entity) } else if let entity = entity as? DrawingStickerEntity { - return DrawingStickerEntityView(context: context, entity: entity) + if case let .file(_, type) = entity.content, case .reaction = type { + return DrawingReactionEntityView(context: context, entity: entity) + } else { + return DrawingStickerEntityView(context: context, entity: entity) + } } else if let entity = entity as? DrawingTextEntity { return DrawingTextEntityView(context: context, entity: entity) } else if let entity = entity as? DrawingVectorEntity { @@ -71,12 +75,14 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView { public var present: (ViewController) -> Void = { _ in } public var push: (ViewController) -> Void = { _ in } + public var canInteract: () -> Bool = { return true } public var hasSelectionChanged: (Bool) -> Void = { _ in } var selectionChanged: (DrawingEntity?) -> Void = { _ in } var requestedMenuForEntityView: (DrawingEntityView, Bool) -> Void = { _, _ in } var entityAdded: (DrawingEntity) -> Void = { _ in } var entityRemoved: (DrawingEntity) -> Void = { _ in } + public var externalEntityRemoved: (DrawingEntity) -> Void = { _ in } var autoSelectEntities = false @@ -513,6 +519,7 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView { if announce { self.entityRemoved(view.entity) + self.externalEntityRemoved(view.entity) } } } @@ -627,6 +634,9 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView { } @objc private func handleTap(_ gestureRecognzier: UITapGestureRecognizer) { + guard self.canInteract() else { + return + } let location = gestureRecognzier.location(in: self) if let entityView = self.entity(at: location) { self.selectEntity(entityView.entity) @@ -751,6 +761,9 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView { } public func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) { + guard self.canInteract() else { + return + } let location = gestureRecognizer.location(in: self) if let selectedEntityView = self.selectedEntityView, let selectionView = selectedEntityView.selectionView { if !self.hasBin { @@ -833,6 +846,9 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView { } public func handlePinch(_ gestureRecognizer: UIPinchGestureRecognizer) { + guard self.canInteract() else { + return + } if !self.hasSelection, let mediaEntityView = self.subviews.first(where: { $0 is DrawingEntityMediaView }) as? DrawingEntityMediaView { mediaEntityView.handlePinch(gestureRecognizer) } else if let selectedEntityView = self.selectedEntityView, let selectionView = selectedEntityView.selectionView { @@ -841,6 +857,9 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView { } public func handleRotate(_ gestureRecognizer: UIRotationGestureRecognizer) { + guard self.canInteract() else { + return + } if !self.hasSelection, let mediaEntityView = self.subviews.first(where: { $0 is DrawingEntityMediaView }) as? DrawingEntityMediaView { mediaEntityView.handleRotate(gestureRecognizer) } else if let selectedEntityView = self.selectedEntityView, let selectionView = selectedEntityView.selectionView { diff --git a/submodules/DrawingUI/Sources/DrawingLocationEntity.swift b/submodules/DrawingUI/Sources/DrawingLocationEntityView.swift similarity index 100% rename from submodules/DrawingUI/Sources/DrawingLocationEntity.swift rename to submodules/DrawingUI/Sources/DrawingLocationEntityView.swift diff --git a/submodules/DrawingUI/Sources/DrawingMediaEntity.swift b/submodules/DrawingUI/Sources/DrawingMediaEntityView.swift similarity index 92% rename from submodules/DrawingUI/Sources/DrawingMediaEntity.swift rename to submodules/DrawingUI/Sources/DrawingMediaEntityView.swift index 1fa43368d61..9bd7aedcc52 100644 --- a/submodules/DrawingUI/Sources/DrawingMediaEntity.swift +++ b/submodules/DrawingUI/Sources/DrawingMediaEntityView.swift @@ -33,6 +33,8 @@ public final class DrawingMediaEntityView: DrawingEntityView, DrawingEntityMedia init(context: AccountContext, entity: DrawingMediaEntity) { super.init(context: context, entity: entity) + self.backgroundColor = UIColor.clear + self.snapTool.onSnapUpdated = { [weak self] type, snapped in if let self { self.onSnapUpdated(type, snapped) @@ -110,10 +112,10 @@ public final class DrawingMediaEntityView: DrawingEntityView, DrawingEntityMedia self.bounds = CGRect(origin: .zero, size: size) self.transform = CGAffineTransformScale(CGAffineTransformMakeRotation(self.mediaEntity.rotation), scale, scale) - if self.previewView?.superview === self { - self.previewView?.layer.transform = CATransform3DMakeScale(self.mediaEntity.mirrored ? -1.0 : 1.0, 1.0, 1.0) - self.previewView?.frame = self.bounds - } +// if self.previewView?.superview === self { +// self.previewView?.layer.transform = CATransform3DMakeScale(self.mediaEntity.mirrored ? -1.0 : 1.0, 1.0, 1.0) +// self.previewView?.frame = self.bounds +// } super.update(animated: animated) @@ -190,4 +192,8 @@ public final class DrawingMediaEntityView: DrawingEntityView, DrawingEntityMedia break } } + + public override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + return self.bounds.contains(point) + } } diff --git a/submodules/DrawingUI/Sources/DrawingReactionView.swift b/submodules/DrawingUI/Sources/DrawingReactionView.swift new file mode 100644 index 00000000000..816f7b3ec1f --- /dev/null +++ b/submodules/DrawingUI/Sources/DrawingReactionView.swift @@ -0,0 +1,348 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import AVFoundation +import Display +import SwiftSignalKit +import TelegramCore +import AnimatedStickerNode +import TelegramAnimatedStickerNode +import StickerResources +import AccountContext +import MediaEditor +import TelegramPresentationData +import ReactionSelectionNode +import UndoUI +import EntityKeyboard +import ComponentFlow + +public class DrawingReactionEntityView: DrawingStickerEntityView { + private var backgroundView: UIImageView + private var outlineView: UIImageView + + override init(context: AccountContext, entity: DrawingStickerEntity) { + let backgroundView = UIImageView(image: UIImage(bundleImageName: "Stories/ReactionShadow")) + backgroundView.layer.zPosition = -1000.0 + + let outlineView = UIImageView(image: UIImage(bundleImageName: "Stories/ReactionOutline")) + outlineView.tintColor = .white + backgroundView.addSubview(outlineView) + + self.backgroundView = backgroundView + self.outlineView = outlineView + + super.init(context: context, entity: entity) + + self.insertSubview(backgroundView, at: 0) + + self.setup() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override var isReaction: Bool { + return true + } + + override func animateInsertion() { + super.animateInsertion() + + Queue.mainQueue().after(0.2) { + let _ = self.selectedTapAction() + } + } + + override func onSelection() { + self.presentReactionSelection() + } + + override func onDeselection() { + let _ = self.dismissReactionSelection() + } + + public override func update(animated: Bool) { + super.update(animated: animated) + + if case let .file(_, type) = self.stickerEntity.content, case let .reaction(_, style) = type { + switch style { + case .white: + self.outlineView.tintColor = .white + case .black: + self.outlineView.tintColor = UIColor(rgb: 0x000000, alpha: 0.5) + } + } + } + + override func updateMirroring(animated: Bool) { + let staticTransform = CATransform3DMakeScale(self.stickerEntity.mirrored ? -1.0 : 1.0, 1.0, 1.0) + if animated { + let isCurrentlyMirrored = ((self.backgroundView.layer.value(forKeyPath: "transform.scale.y") as? NSNumber)?.floatValue ?? 1.0) < 0.0 + var animationSourceTransform = CATransform3DIdentity + var animationTargetTransform = CATransform3DIdentity + if isCurrentlyMirrored { + animationSourceTransform = CATransform3DRotate(animationSourceTransform, .pi, 0.0, 1.0, 0.0) + animationSourceTransform.m34 = -1.0 / self.imageNode.frame.width + } + if self.stickerEntity.mirrored { + animationTargetTransform = CATransform3DRotate(animationTargetTransform, .pi, 0.0, 1.0, 0.0) + animationTargetTransform.m34 = -1.0 / self.imageNode.frame.width + } + self.backgroundView.layer.transform = animationSourceTransform + + let values = [1.0, 0.01, 1.0] + let keyTimes = [0.0, 0.5, 1.0] + self.animationNode?.layer.animateKeyframes(values: values as [NSNumber], keyTimes: keyTimes as [NSNumber], duration: 0.25, keyPath: "transform.scale.x", timingFunction: CAMediaTimingFunctionName.linear.rawValue) + + UIView.animate(withDuration: 0.25, animations: { + self.backgroundView.layer.transform = animationTargetTransform + }, completion: { finished in + self.backgroundView.layer.transform = staticTransform + }) + } else { + CATransaction.begin() + CATransaction.setDisableActions(true) + self.backgroundView.layer.transform = staticTransform + CATransaction.commit() + } + } + + private weak var reactionContextNode: ReactionContextNode? + fileprivate func presentReactionSelection() { + guard let containerView = self.containerView, let superview = containerView.superview?.superview?.superview?.superview, self.reactionContextNode == nil else { + return + } + + let availableSize = superview.frame.size + let reactionItems = containerView.getAvailableReactions() + + let insets = UIEdgeInsets(top: 64.0, left: 0.0, bottom: 64.0, right: 0.0) + + let layout: (ContainedViewLayoutTransition) -> Void = { [weak self, weak superview] transition in + guard let self, let superview, let reactionContextNode = self.reactionContextNode else { + return + } + let anchorRect = self.convert(self.bounds, to: superview).offsetBy(dx: 0.0, dy: -20.0) + reactionContextNode.updateLayout(size: availableSize, insets: insets, anchorRect: anchorRect, centerAligned: true, isCoveredByInput: false, isAnimatingOut: false, transition: transition) + } + + let reactionContextNodeTransition: Transition = .immediate + let reactionContextNode: ReactionContextNode + reactionContextNode = ReactionContextNode( + context: self.context, + animationCache: self.context.animationCache, + presentationData: self.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkPresentationTheme), + items: reactionItems.map(ReactionContextItem.reaction), + selectedItems: Set(), + title: nil, + alwaysAllowPremiumReactions: false, + getEmojiContent: { [weak self] animationCache, animationRenderer in + guard let self else { + preconditionFailure() + } + + let mappedReactionItems: [EmojiComponentReactionItem] = reactionItems.map { reaction -> EmojiComponentReactionItem in + return EmojiComponentReactionItem(reaction: reaction.reaction.rawValue, file: reaction.stillAnimation) + } + + return EmojiPagerContentComponent.emojiInputData( + context: self.context, + animationCache: animationCache, + animationRenderer: animationRenderer, + isStandalone: false, + subject: .reaction(onlyTop: false), + hasTrending: false, + topReactionItems: mappedReactionItems, + areUnicodeEmojiEnabled: false, + areCustomEmojiEnabled: true, + chatPeerId: self.context.account.peerId, + selectedItems: Set(), + premiumIfSavedMessages: false + ) + }, + isExpandedUpdated: { transition in + layout(transition) + }, + requestLayout: { transition in + layout(transition) + }, + requestUpdateOverlayWantsToBeBelowKeyboard: { transition in + layout(transition) + } + ) + reactionContextNode.displayTail = true + reactionContextNode.forceTailToRight = true + reactionContextNode.forceDark = true + self.reactionContextNode = reactionContextNode + + reactionContextNode.reactionSelected = { [weak self] updateReaction, _ in + guard let self else { + return + } + + let continueWithAnimationFile: (TelegramMediaFile) -> Void = { [weak self] animation in + guard let self else { + return + } + + if case let .file(_, type) = self.stickerEntity.content, case let .reaction(_, style) = type { + self.stickerEntity.content = .file(animation, .reaction(updateReaction.reaction, style)) + } + + var nodeToTransitionOut: ASDisplayNode? + if let animationNode = self.animationNode { + nodeToTransitionOut = animationNode + } else if !self.imageNode.isHidden { + nodeToTransitionOut = self.imageNode + } + + if let nodeToTransitionOut, let snapshot = nodeToTransitionOut.view.snapshotView(afterScreenUpdates: false) { + snapshot.frame = nodeToTransitionOut.frame + snapshot.layer.transform = nodeToTransitionOut.transform + snapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + snapshot.removeFromSuperview() + }) + snapshot.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2) + self.addSubview(snapshot) + } + + self.animationNode?.removeFromSupernode() + self.animationNode = nil + self.didSetUpAnimationNode = false + self.isPlaying = false + self.currentSize = nil + + self.setup() + self.applyVisibility() + self.setNeedsLayout() + + let nodeToTransitionIn: ASDisplayNode? + if let animationNode = self.animationNode { + nodeToTransitionIn = animationNode + } else { + nodeToTransitionIn = self.imageNode + } + + if let nodeToTransitionIn { + nodeToTransitionIn.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + nodeToTransitionIn.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2) + } + + let _ = self.dismissReactionSelection() + } + + switch updateReaction { + case .builtin: + let _ = (self.context.engine.stickers.availableReactions() + |> take(1) + |> deliverOnMainQueue).start(next: { availableReactions in + guard let availableReactions else { + return + } + var animation: TelegramMediaFile? + for reaction in availableReactions.reactions { + if reaction.value == updateReaction.reaction { + animation = reaction.selectAnimation + break + } + } + if let animation { + continueWithAnimationFile(animation) + } + }) + case let .custom(fileId, file): + if let file { + continueWithAnimationFile(file) + } else { + let _ = (self.context.engine.stickers.resolveInlineStickers(fileIds: [fileId]) + |> deliverOnMainQueue).start(next: { files in + if let itemFile = files[fileId] { + continueWithAnimationFile(itemFile) + } + }) + } + } + } + + reactionContextNode.premiumReactionsSelected = { [weak self] file in + guard let self else { + return + } + + if let file { + let context = self.context + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + + let controller = UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, loop: true, title: nil, text: presentationData.strings.Story_Editor_TooltipPremiumReaction, undoText: nil, customAction: nil), elevatedLayout: true, animateInAsReplacement: false, blurred: true, action: { [weak self] action in + if case .info = action, let self { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .storiesExpirationDurations, forceDark: true, dismissed: nil) + self.containerView?.push(controller) + } + return false + }) + self.containerView?.present(controller) + } else { + let controller = self.context.sharedContext.makePremiumIntroController(context: self.context, source: .storiesExpirationDurations, forceDark: true, dismissed: nil) + self.containerView?.push(controller) + } + } + + let anchorRect = self.convert(self.bounds, to: superview).offsetBy(dx: 0.0, dy: -20.0) + reactionContextNodeTransition.setFrame(view: reactionContextNode.view, frame: CGRect(origin: CGPoint(), size: availableSize)) + reactionContextNode.updateLayout(size: availableSize, insets: insets, anchorRect: anchorRect, centerAligned: true, isCoveredByInput: false, isAnimatingOut: false, transition: reactionContextNodeTransition.containedViewLayoutTransition) + + superview.addSubnode(reactionContextNode) + reactionContextNode.animateIn(from: anchorRect) + } + + fileprivate func dismissReactionSelection() -> Bool { + if let reactionContextNode = self.reactionContextNode { + reactionContextNode.animateOut(to: nil, animatingOutToReaction: false) + self.reactionContextNode = nil + + Queue.mainQueue().after(0.35) { + reactionContextNode.view.removeFromSuperview() + } + + return false + } else { + return true + } + } + + override func selectedTapAction() -> Bool { + if case let .file(file, type) = self.stickerEntity.content, case let .reaction(reaction, style) = type { + guard self.reactionContextNode == nil else { + let values = [self.entity.scale, self.entity.scale * 0.93, self.entity.scale] + let keyTimes = [0.0, 0.33, 1.0] + self.layer.animateKeyframes(values: values as [NSNumber], keyTimes: keyTimes as [NSNumber], duration: 0.3, keyPath: "transform.scale") + + let updatedStyle: DrawingStickerEntity.Content.FileType.ReactionStyle + switch style { + case .white: + updatedStyle = .black + case .black: + updatedStyle = .white + } + self.stickerEntity.content = .file(file, .reaction(reaction, updatedStyle)) + + self.update(animated: false) + + return true + } + + self.presentReactionSelection() + + return true + } else { + return super.selectedTapAction() + } + } + + override func innerLayoutSubview(boundingSize: CGSize) -> CGSize { + self.backgroundView.frame = CGRect(origin: .zero, size: boundingSize).insetBy(dx: -5.0, dy: -5.0) + self.outlineView.frame = backgroundView.bounds + return CGSize(width: floor(boundingSize.width * 0.63), height: floor(boundingSize.width * 0.63)) + } +} diff --git a/submodules/DrawingUI/Sources/DrawingScreen.swift b/submodules/DrawingUI/Sources/DrawingScreen.swift index cb446a9c475..385984dbaa4 100644 --- a/submodules/DrawingUI/Sources/DrawingScreen.swift +++ b/submodules/DrawingUI/Sources/DrawingScreen.swift @@ -2460,6 +2460,9 @@ public class DrawingScreen: ViewController, TGPhotoDrawingInterfaceController, U }, onTextEditingEnded: { _ in }, editEntity: { _ in }, + shouldDeleteEntity: { _ in + return true + }, getCurrentImage: { [weak controller] in return controller?.getCurrentImage() }, @@ -2981,7 +2984,8 @@ public final class DrawingToolsInteraction { private let onInteractionUpdated: (Bool) -> Void private let onTextEditingEnded: (Bool) -> Void private let editEntity: (DrawingEntity) -> Void - + private let shouldDeleteEntity: (DrawingEntity) -> Bool + public let getCurrentImage: () -> UIImage? private let getControllerNode: () -> ASDisplayNode? private let present: (ViewController, PresentationContextType, Any?) -> Void @@ -3012,6 +3016,7 @@ public final class DrawingToolsInteraction { onInteractionUpdated: @escaping (Bool) -> Void, onTextEditingEnded: @escaping (Bool) -> Void, editEntity: @escaping (DrawingEntity) -> Void, + shouldDeleteEntity: @escaping (DrawingEntity) -> Bool, getCurrentImage: @escaping () -> UIImage?, getControllerNode: @escaping () -> ASDisplayNode?, present: @escaping (ViewController, PresentationContextType, Any?) -> Void, @@ -3030,6 +3035,7 @@ public final class DrawingToolsInteraction { self.onInteractionUpdated = onInteractionUpdated self.onTextEditingEnded = onTextEditingEnded self.editEntity = editEntity + self.shouldDeleteEntity = shouldDeleteEntity self.getCurrentImage = getCurrentImage self.getControllerNode = getControllerNode self.present = present @@ -3072,13 +3078,15 @@ public final class DrawingToolsInteraction { } var isVideo = false + var isAdditional = false if let entity = entityView.entity as? DrawingStickerEntity { - if case .dualVideoReference = entity.content { + if case let .dualVideoReference(isAdditionalValue) = entity.content { isVideo = true + isAdditional = isAdditionalValue } } - guard !isVideo else { + guard !isVideo || isAdditional else { return } @@ -3086,7 +3094,9 @@ public final class DrawingToolsInteraction { var actions: [ContextMenuAction] = [] actions.append(ContextMenuAction(content: .text(title: presentationData.strings.Paint_Delete, accessibilityLabel: presentationData.strings.Paint_Delete), action: { [weak self, weak entityView] in if let self, let entityView { - self.entitiesView.remove(uuid: entityView.entity.uuid, animated: true) + if self.shouldDeleteEntity(entityView.entity) { + self.entitiesView.remove(uuid: entityView.entity.uuid, animated: true) + } } })) if let entityView = entityView as? DrawingLocationEntityView { @@ -3103,7 +3113,7 @@ public final class DrawingToolsInteraction { self.entitiesView.selectEntity(entityView.entity) } })) - } else if entityView is DrawingStickerEntityView || entityView is DrawingBubbleEntityView { + } else if (entityView is DrawingStickerEntityView || entityView is DrawingBubbleEntityView) && !isVideo { actions.append(ContextMenuAction(content: .text(title: presentationData.strings.Paint_Flip, accessibilityLabel: presentationData.strings.Paint_Flip), action: { [weak self] in if let self { self.flipSelectedEntity() diff --git a/submodules/DrawingUI/Sources/DrawingSimpleShapeEntity.swift b/submodules/DrawingUI/Sources/DrawingSimpleShapeEntityView.swift similarity index 100% rename from submodules/DrawingUI/Sources/DrawingSimpleShapeEntity.swift rename to submodules/DrawingUI/Sources/DrawingSimpleShapeEntityView.swift diff --git a/submodules/DrawingUI/Sources/DrawingStickerEntity.swift b/submodules/DrawingUI/Sources/DrawingStickerEntityView.swift similarity index 70% rename from submodules/DrawingUI/Sources/DrawingStickerEntity.swift rename to submodules/DrawingUI/Sources/DrawingStickerEntityView.swift index 0aafe33323b..5e21b7c1244 100644 --- a/submodules/DrawingUI/Sources/DrawingStickerEntity.swift +++ b/submodules/DrawingUI/Sources/DrawingStickerEntityView.swift @@ -13,54 +13,79 @@ import MediaEditor import UniversalMediaPlayer import TelegramPresentationData import TelegramUniversalVideoContent -import ReactionSelectionNode -import UndoUI -import EntityKeyboard -import ComponentFlow -public final class DrawingStickerEntityView: DrawingEntityView { - private var stickerEntity: DrawingStickerEntity { - return self.entity as! DrawingStickerEntity +private class BlurView: UIVisualEffectView { + private func setup() { + for subview in self.subviews { + if subview.description.contains("VisualEffectSubview") { + subview.isHidden = true + } + } + + if let sublayer = self.layer.sublayers?[0], let filters = sublayer.filters { + sublayer.backgroundColor = nil + sublayer.isOpaque = false + let allowedKeys: [String] = [ + "gaussianBlur" + ] + sublayer.filters = filters.filter { filter in + guard let filter = filter as? NSObject else { + return true + } + let filterName = String(describing: filter) + if !allowedKeys.contains(filterName) { + return false + } + return true + } + } } - var started: ((Double) -> Void)? + override var effect: UIVisualEffect? { + get { + return super.effect + } + set { + super.effect = newValue + self.setup() + } + } - public var updated: () -> Void = {} + override func didAddSubview(_ subview: UIView) { + super.didAddSubview(subview) + self.setup() + } +} + +public class DrawingStickerEntityView: DrawingEntityView { + var stickerEntity: DrawingStickerEntity { + return self.entity as! DrawingStickerEntity + } - private var currentSize: CGSize? + let imageNode: TransformImageNode + var animationNode: DefaultAnimatedStickerNodeImpl? + var videoNode: UniversalVideoNode? + var cameraPreviewView: UIView? - private var backgroundView: UIImageView? - private var outlineView: UIImageView? + let progressDisposable = MetaDisposable() + let progressLayer = CAShapeLayer() - private let imageNode: TransformImageNode - private var animationNode: DefaultAnimatedStickerNodeImpl? - private var videoNode: UniversalVideoNode? - - private var didSetUpAnimationNode = false + var didSetUpAnimationNode = false private let stickerFetchedDisposable = MetaDisposable() private let cachedDisposable = MetaDisposable() private var isVisible = true - private var isPlaying = false + var isPlaying = false + var started: ((Double) -> Void)? + + var currentSize: CGSize? + public var updated: () -> Void = {} init(context: AccountContext, entity: DrawingStickerEntity) { self.imageNode = TransformImageNode() super.init(context: context, entity: entity) - if case .file(_, .reaction) = entity.content { - let backgroundView = UIImageView(image: UIImage(bundleImageName: "Stories/ReactionShadow")) - backgroundView.layer.zPosition = -1000.0 - - let outlineView = UIImageView(image: UIImage(bundleImageName: "Stories/ReactionOutline")) - outlineView.tintColor = .white - backgroundView.addSubview(outlineView) - - self.addSubview(backgroundView) - self.backgroundView = backgroundView - self.outlineView = outlineView - } - self.addSubview(self.imageNode.view) self.setup() @@ -73,6 +98,7 @@ public final class DrawingStickerEntityView: DrawingEntityView { deinit { self.stickerFetchedDisposable.dispose() self.cachedDisposable.dispose() + self.progressDisposable.dispose() } private var file: TelegramMediaFile? { @@ -103,7 +129,6 @@ public final class DrawingStickerEntityView: DrawingEntityView { return image } - private var video: TelegramMediaFile? { if case let .video(file) = self.stickerEntity.content { return file @@ -143,7 +168,7 @@ public final class DrawingStickerEntityView: DrawingEntityView { self.animationNode?.dynamicColor = color } - private func setup() { + func setup() { if let file = self.file { if let dimensions = file.dimensions { if file.isAnimatedSticker || file.isVideoSticker || file.mimeType == "video/webm" { @@ -308,7 +333,7 @@ public final class DrawingStickerEntityView: DrawingEntityView { self.applyVisibility() } - private func applyVisibility() { + func applyVisibility() { let isPlaying = self.isVisible if self.isPlaying != isPlaying { self.isPlaying = isPlaying @@ -328,6 +353,110 @@ public final class DrawingStickerEntityView: DrawingEntityView { } } self.animationNode?.visibility = isPlaying + if isPlaying { + self.animationNode?.play() + } + } + } + + public func setupCameraPreviewView(_ cameraPreviewView: UIView, progress: Signal) { + self.addSubview(cameraPreviewView) + self.cameraPreviewView = cameraPreviewView + + self.progressLayer.opacity = 1.0 + self.progressLayer.transform = CATransform3DMakeRotation(-.pi / 2.0, 0.0, 0.0, 1.0) + self.progressLayer.fillColor = UIColor.clear.cgColor + self.progressLayer.strokeColor = UIColor(rgb: 0xffffff, alpha: 0.5).cgColor + self.progressLayer.lineWidth = 3.0 + self.progressLayer.lineCap = .round + self.progressLayer.strokeEnd = 0.0 + self.layer.addSublayer(self.progressLayer) + + self.setNeedsLayout() + + self.progressDisposable.set((progress + |> deliverOnMainQueue).startStrict(next: { [weak self] progress in + if let self { + self.progressLayer.strokeEnd = CGFloat(progress) + } + })) + } + + public func invalidateCameraPreviewView() { + guard let cameraPreviewView = self.cameraPreviewView else { + return + } + Queue.mainQueue().after(0.1, { + self.cameraPreviewView = nil + cameraPreviewView.removeFromSuperview() + + if let cameraSnapshotView = self.cameraSnapshotView { + self.cameraSnapshotView = nil + UIView.animate(withDuration: 0.25, animations: { + cameraSnapshotView.alpha = 0.0 + }, completion: { _ in + cameraSnapshotView.removeFromSuperview() + }) + } + }) + self.progressLayer.opacity = 0.0 + self.progressLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { _ in + self.progressLayer.removeFromSuperlayer() + self.progressLayer.path = nil + }) + self.progressDisposable.set(nil) + } + + public func snapshotCameraPreviewView() { + guard let cameraPreviewView = self.cameraPreviewView else { + return + } + if let snapshot = cameraPreviewView.snapshotView(afterScreenUpdates: false) { + self.cameraSnapshotView = snapshot + self.addSubview(snapshot) + } + self.layer.addSublayer(self.progressLayer) + } + + private var cameraBlurView: BlurView? + private var cameraSnapshotView: UIView? + public func beginCameraSwitch() { + guard let cameraPreviewView = self.cameraPreviewView, self.cameraBlurView == nil else { + return + } + if let snapshot = cameraPreviewView.snapshotView(afterScreenUpdates: false) { + self.cameraSnapshotView = snapshot + self.addSubview(snapshot) + } + + let blurView = BlurView(effect: nil) + blurView.clipsToBounds = true + blurView.frame = self.bounds + blurView.layer.cornerRadius = self.bounds.width / 2.0 + self.addSubview(blurView) + UIView.transition(with: self, duration: 0.4, options: [.transitionFlipFromLeft, .curveEaseOut], animations: { + blurView.effect = UIBlurEffect(style: .dark) + }) + self.cameraBlurView = blurView + } + + public func commitCameraSwitch() { + if let cameraBlurView = self.cameraBlurView { + self.cameraBlurView = nil + UIView.animate(withDuration: 0.4, animations: { + cameraBlurView.effect = nil + }, completion: { _ in + cameraBlurView.removeFromSuperview() + }) + } + + if let cameraSnapshotView = self.cameraSnapshotView { + self.cameraSnapshotView = nil + UIView.animate(withDuration: 0.25, animations: { + cameraSnapshotView.alpha = 0.0 + }, completion: { _ in + cameraSnapshotView.removeFromSuperview() + }) } } @@ -341,14 +470,8 @@ public final class DrawingStickerEntityView: DrawingEntityView { self.currentSize = size let sideSize: CGFloat = max(size.width, size.height) - var boundingSize = CGSize(width: sideSize, height: sideSize) - - if let backgroundView = self.backgroundView, let outlineView = self.outlineView { - backgroundView.frame = CGRect(origin: .zero, size: boundingSize).insetBy(dx: -5.0, dy: -5.0) - outlineView.frame = backgroundView.bounds - boundingSize = CGSize(width: floor(sideSize * 0.63), height: floor(sideSize * 0.63)) - } - + let boundingSize = self.innerLayoutSubview(boundingSize: CGSize(width: sideSize, height: sideSize)) + let imageSize = self.dimensions.aspectFitted(boundingSize) let imageFrame = CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: (size.height - imageSize.height) / 2.0), size: imageSize) @@ -373,271 +496,32 @@ public final class DrawingStickerEntityView: DrawingEntityView { videoNode.updateLayout(size: imageSize, transition: .immediate) } - self.update(animated: false) - } - } - - private var isReaction: Bool { - if case let .file(_, type) = self.stickerEntity.content, case .reaction = type { - return true - } else { - return false - } - } - - override func animateInsertion() { - super.animateInsertion() - - if self.isReaction { - Queue.mainQueue().after(0.2) { - let _ = self.selectedTapAction() + if let cameraPreviewView = self.cameraPreviewView { + cameraPreviewView.layer.cornerRadius = imageSize.width / 2.0 + cameraPreviewView.frame = CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) * 0.5), y: floor((size.height - imageSize.height) * 0.5)), size: imageSize) + self.progressLayer.frame = cameraPreviewView.frame + + if self.progressLayer.path == nil { + self.progressLayer.path = CGPath(ellipseIn: cameraPreviewView.frame.insetBy(dx: 6.0, dy: 6.0), transform: nil) + } } + + self.update(animated: false) } } - override func onSelection() { - if self.isReaction { - self.presentReactionSelection() - } + var isReaction: Bool { + return false } func onDeselection() { - if self.isReaction { - let _ = self.dismissReactionSelection() - } - } - - private weak var reactionContextNode: ReactionContextNode? - fileprivate func presentReactionSelection() { - guard let containerView = self.containerView, let superview = containerView.superview?.superview?.superview?.superview, self.reactionContextNode == nil else { - return - } - - let availableSize = superview.frame.size - let reactionItems = containerView.getAvailableReactions() - - let insets = UIEdgeInsets(top: 64.0, left: 0.0, bottom: 64.0, right: 0.0) - - let layout: (ContainedViewLayoutTransition) -> Void = { [weak self, weak superview] transition in - guard let self, let superview, let reactionContextNode = self.reactionContextNode else { - return - } - let anchorRect = self.convert(self.bounds, to: superview).offsetBy(dx: 0.0, dy: -20.0) - reactionContextNode.updateLayout(size: availableSize, insets: insets, anchorRect: anchorRect, centerAligned: true, isCoveredByInput: false, isAnimatingOut: false, transition: transition) - } - let reactionContextNodeTransition: Transition = .immediate - let reactionContextNode: ReactionContextNode - reactionContextNode = ReactionContextNode( - context: self.context, - animationCache: self.context.animationCache, - presentationData: self.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkPresentationTheme), - items: reactionItems.map(ReactionContextItem.reaction), - selectedItems: Set(), - title: nil, - getEmojiContent: { [weak self] animationCache, animationRenderer in - guard let self else { - preconditionFailure() - } - - let mappedReactionItems: [EmojiComponentReactionItem] = reactionItems.map { reaction -> EmojiComponentReactionItem in - return EmojiComponentReactionItem(reaction: reaction.reaction.rawValue, file: reaction.stillAnimation) - } - - return EmojiPagerContentComponent.emojiInputData( - context: self.context, - animationCache: animationCache, - animationRenderer: animationRenderer, - isStandalone: false, - subject: .reaction, - hasTrending: false, - topReactionItems: mappedReactionItems, - areUnicodeEmojiEnabled: false, - areCustomEmojiEnabled: true, - chatPeerId: self.context.account.peerId, - selectedItems: Set(), - premiumIfSavedMessages: false - ) - }, - isExpandedUpdated: { transition in - layout(transition) - }, - requestLayout: { transition in - layout(transition) - }, - requestUpdateOverlayWantsToBeBelowKeyboard: { transition in - layout(transition) - } - ) - reactionContextNode.displayTail = true - reactionContextNode.forceTailToRight = true - reactionContextNode.forceDark = true - self.reactionContextNode = reactionContextNode - - reactionContextNode.reactionSelected = { [weak self] updateReaction, _ in - guard let self else { - return - } - - let continueWithAnimationFile: (TelegramMediaFile) -> Void = { [weak self] animation in - guard let self else { - return - } - - if case let .file(_, type) = self.stickerEntity.content, case let .reaction(_, style) = type { - self.stickerEntity.content = .file(animation, .reaction(updateReaction.reaction, style)) - } - - var nodeToTransitionOut: ASDisplayNode? - if let animationNode = self.animationNode { - nodeToTransitionOut = animationNode - } else if !self.imageNode.isHidden { - nodeToTransitionOut = self.imageNode - } - - if let nodeToTransitionOut, let snapshot = nodeToTransitionOut.view.snapshotView(afterScreenUpdates: false) { - snapshot.frame = nodeToTransitionOut.frame - snapshot.layer.transform = nodeToTransitionOut.transform - snapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in - snapshot.removeFromSuperview() - }) - snapshot.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2) - self.addSubview(snapshot) - } - - self.animationNode?.removeFromSupernode() - self.animationNode = nil - self.didSetUpAnimationNode = false - self.isPlaying = false - self.currentSize = nil - - self.setup() - self.applyVisibility() - self.setNeedsLayout() - - let nodeToTransitionIn: ASDisplayNode? - if let animationNode = self.animationNode { - nodeToTransitionIn = animationNode - } else { - nodeToTransitionIn = self.imageNode - } - - if let nodeToTransitionIn { - nodeToTransitionIn.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - nodeToTransitionIn.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2) - } - - let _ = self.dismissReactionSelection() - } - - switch updateReaction { - case .builtin: - let _ = (self.context.engine.stickers.availableReactions() - |> take(1) - |> deliverOnMainQueue).start(next: { availableReactions in - guard let availableReactions else { - return - } - var animation: TelegramMediaFile? - for reaction in availableReactions.reactions { - if reaction.value == updateReaction.reaction { - animation = reaction.selectAnimation - break - } - } - if let animation { - continueWithAnimationFile(animation) - } - }) - case let .custom(fileId, file): - if let file { - continueWithAnimationFile(file) - } else { - let _ = (self.context.engine.stickers.resolveInlineStickers(fileIds: [fileId]) - |> deliverOnMainQueue).start(next: { files in - if let itemFile = files[fileId] { - continueWithAnimationFile(itemFile) - } - }) - } - } - } - - reactionContextNode.premiumReactionsSelected = { [weak self] file in - guard let self else { - return - } - - if let file { - let context = self.context - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - - let controller = UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, loop: true, title: nil, text: presentationData.strings.Story_Editor_TooltipPremiumReaction, undoText: nil, customAction: nil), elevatedLayout: true, animateInAsReplacement: false, blurred: true, action: { [weak self] action in - if case .info = action, let self { - let controller = context.sharedContext.makePremiumIntroController(context: context, source: .storiesExpirationDurations, forceDark: true, dismissed: nil) - self.containerView?.push(controller) - } - return false - }) - self.containerView?.present(controller) - } else { - let controller = self.context.sharedContext.makePremiumIntroController(context: self.context, source: .storiesExpirationDurations, forceDark: true, dismissed: nil) - self.containerView?.push(controller) - } - } - - let anchorRect = self.convert(self.bounds, to: superview).offsetBy(dx: 0.0, dy: -20.0) - reactionContextNodeTransition.setFrame(view: reactionContextNode.view, frame: CGRect(origin: CGPoint(), size: availableSize)) - reactionContextNode.updateLayout(size: availableSize, insets: insets, anchorRect: anchorRect, centerAligned: true, isCoveredByInput: false, isAnimatingOut: false, transition: reactionContextNodeTransition.containedViewLayoutTransition) - - superview.addSubnode(reactionContextNode) - reactionContextNode.animateIn(from: anchorRect) } - fileprivate func dismissReactionSelection() -> Bool { - if let reactionContextNode = self.reactionContextNode { - reactionContextNode.animateOut(to: nil, animatingOutToReaction: false) - self.reactionContextNode = nil - - Queue.mainQueue().after(0.35) { - reactionContextNode.view.removeFromSuperview() - } - - return false - } else { - return true - } + func innerLayoutSubview(boundingSize: CGSize) -> CGSize { + return boundingSize } - - override func selectedTapAction() -> Bool { - if case let .file(file, type) = self.stickerEntity.content, case let .reaction(reaction, style) = type { - guard self.reactionContextNode == nil else { - let values = [self.entity.scale, self.entity.scale * 0.93, self.entity.scale] - let keyTimes = [0.0, 0.33, 1.0] - self.layer.animateKeyframes(values: values as [NSNumber], keyTimes: keyTimes as [NSNumber], duration: 0.3, keyPath: "transform.scale") - - let updatedStyle: DrawingStickerEntity.Content.FileType.ReactionStyle - switch style { - case .white: - updatedStyle = .black - case .black: - updatedStyle = .white - } - self.stickerEntity.content = .file(file, .reaction(reaction, updatedStyle)) - - self.update(animated: false) - - return true - } - - self.presentReactionSelection() - - return true - } else { - return super.selectedTapAction() - } - } - + public override func update(animated: Bool) { self.center = self.stickerEntity.position @@ -646,24 +530,19 @@ public final class DrawingStickerEntityView: DrawingEntityView { self.bounds = CGRect(origin: .zero, size: self.dimensions.aspectFitted(size)) self.transform = CGAffineTransformScale(CGAffineTransformMakeRotation(self.stickerEntity.rotation), self.stickerEntity.scale, self.stickerEntity.scale) - if case let .file(_, type) = self.stickerEntity.content, case let .reaction(_, style) = type { - switch style { - case .white: - self.outlineView?.tintColor = .white - case .black: - self.outlineView?.tintColor = UIColor(rgb: 0x000000, alpha: 0.5) - } - } self.updateAnimationColor() - let isReaction = self.isReaction - let staticTransform = CATransform3DMakeScale(self.stickerEntity.mirrored ? -1.0 : 1.0, 1.0, 1.0) + self.updateMirroring(animated: animated) + self.updated() + + super.update(animated: animated) + } + + func updateMirroring(animated: Bool) { + let staticTransform = CATransform3DMakeScale(self.stickerEntity.mirrored ? -1.0 : 1.0, 1.0, 1.0) if animated { - var isCurrentlyMirrored = ((self.imageNode.layer.value(forKeyPath: "transform.scale.y") as? NSNumber)?.floatValue ?? 1.0) < 0.0 - if isReaction { - isCurrentlyMirrored = ((self.backgroundView?.layer.value(forKeyPath: "transform.scale.y") as? NSNumber)?.floatValue ?? 1.0) < 0.0 - } + let isCurrentlyMirrored = ((self.imageNode.layer.value(forKeyPath: "transform.scale.y") as? NSNumber)?.floatValue ?? 1.0) < 0.0 var animationSourceTransform = CATransform3DIdentity var animationTargetTransform = CATransform3DIdentity if isCurrentlyMirrored { @@ -674,50 +553,28 @@ public final class DrawingStickerEntityView: DrawingEntityView { animationTargetTransform = CATransform3DRotate(animationTargetTransform, .pi, 0.0, 1.0, 0.0) animationTargetTransform.m34 = -1.0 / self.imageNode.frame.width } - if isReaction { - self.backgroundView?.layer.transform = animationSourceTransform - - let values = [1.0, 0.01, 1.0] - let keyTimes = [0.0, 0.5, 1.0] - self.animationNode?.layer.animateKeyframes(values: values as [NSNumber], keyTimes: keyTimes as [NSNumber], duration: 0.25, keyPath: "transform.scale.x", timingFunction: CAMediaTimingFunctionName.linear.rawValue) - } else { - self.imageNode.transform = animationSourceTransform - self.animationNode?.transform = animationSourceTransform - self.videoNode?.transform = animationSourceTransform - } + + self.imageNode.transform = animationSourceTransform + self.animationNode?.transform = animationSourceTransform + self.videoNode?.transform = animationSourceTransform + UIView.animate(withDuration: 0.25, animations: { - if isReaction { - self.backgroundView?.layer.transform = animationTargetTransform - } else { - self.imageNode.transform = animationTargetTransform - self.animationNode?.transform = animationTargetTransform - self.videoNode?.transform = animationTargetTransform - } + self.imageNode.transform = animationTargetTransform + self.animationNode?.transform = animationTargetTransform + self.videoNode?.transform = animationTargetTransform }, completion: { finished in - if isReaction { - self.backgroundView?.layer.transform = staticTransform - } else { - self.imageNode.transform = staticTransform - self.animationNode?.transform = staticTransform - self.videoNode?.transform = staticTransform - } + self.imageNode.transform = staticTransform + self.animationNode?.transform = staticTransform + self.videoNode?.transform = staticTransform }) } else { CATransaction.begin() CATransaction.setDisableActions(true) - if isReaction { - self.backgroundView?.layer.transform = staticTransform - } else { - self.imageNode.transform = staticTransform - self.animationNode?.transform = staticTransform - self.videoNode?.transform = staticTransform - } + self.imageNode.transform = staticTransform + self.animationNode?.transform = staticTransform + self.videoNode?.transform = staticTransform CATransaction.commit() } - - self.updated() - - super.update(animated: animated) } override func updateSelectionView() { @@ -838,7 +695,7 @@ final class DrawingStickerEntititySelectionView: DrawingEntitySelectionView { self.snapTool.maybeSkipFromStart(entityView: entityView, position: entity.position) - let _ = entityView.dismissReactionSelection() + entityView.onDeselection() if let sublayers = self.layer.sublayers { for layer in sublayers { @@ -927,7 +784,7 @@ final class DrawingStickerEntititySelectionView: DrawingEntitySelectionView { switch gestureRecognizer.state { case .began, .changed: - let _ = entityView.dismissReactionSelection() + entityView.onDeselection() if case .began = gestureRecognizer.state { entityView.onInteractionUpdated(true) @@ -961,7 +818,7 @@ final class DrawingStickerEntititySelectionView: DrawingEntitySelectionView { switch gestureRecognizer.state { case .began: - let _ = entityView.dismissReactionSelection() + entityView.onDeselection() self.snapTool.maybeSkipFromStart(entityView: entityView, rotation: entity.rotation) entityView.onInteractionUpdated(true) diff --git a/submodules/DrawingUI/Sources/DrawingTextEntity.swift b/submodules/DrawingUI/Sources/DrawingTextEntityView.swift similarity index 96% rename from submodules/DrawingUI/Sources/DrawingTextEntity.swift rename to submodules/DrawingUI/Sources/DrawingTextEntityView.swift index 0388e89aeb5..02be4d4ebcc 100644 --- a/submodules/DrawingUI/Sources/DrawingTextEntity.swift +++ b/submodules/DrawingUI/Sources/DrawingTextEntityView.swift @@ -80,10 +80,6 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate fatalError("init(coder:) has not been implemented") } - deinit { - self.displayLink?.invalidate() - } - override func animateInsertion() { } @@ -516,50 +512,6 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate self.updateTextAnimations() } } - - private var previousDisplayLinkTime: Double? - - private var displayLinkStart: Double? - private var displayLink: SharedDisplayLinkDriver.Link? - - private var pendingImage: (Double, UIImage)? - private var cachedFrames: [DrawingTextEntity.AnimationFrame] = [] - - private func setupRecorder(delta: Double, duration: Double) { - self.cachedFrames.removeAll() - - self.displayLink?.invalidate() - self.displayLink = nil - - self.previousDisplayLinkTime = nil - let displayLinkStart = CACurrentMediaTime() - self.displayLinkStart = displayLinkStart - - self.displayLink = SharedDisplayLinkDriver.shared.add { [weak self] in - if let strongSelf = self { - let currentTime = CACurrentMediaTime() - if let previousDisplayLinkTime = strongSelf.previousDisplayLinkTime, currentTime < previousDisplayLinkTime + delta { - return - } - if currentTime >= displayLinkStart + duration { - strongSelf.displayLink?.invalidate() - strongSelf.displayLink = nil - } - if let (timestamp, image) = strongSelf.pendingImage, let previousDisplayLinkTime = strongSelf.previousDisplayLinkTime { - strongSelf.cachedFrames.append(DrawingTextEntity.AnimationFrame(timestamp: timestamp - displayLinkStart, duration: currentTime - previousDisplayLinkTime, image: image)) - } - if let image = strongSelf.getPresentationRenderImage() { - strongSelf.pendingImage = (currentTime, image) - } - if strongSelf.previousDisplayLinkTime == nil { - strongSelf.previousDisplayLinkTime = displayLinkStart - } else { - strongSelf.previousDisplayLinkTime = currentTime - } - } - } - self.displayLink?.isPaused = false - } func updateTextAnimations() { for layer in self.textView.characterLayers { @@ -587,7 +539,6 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate layer.add(animation, forKey: "opacity") offset += delta } - self.setupRecorder(delta: delta, duration: duration) case .wiggle: for layer in self.textView.characterLayers { let animation = CABasicAnimation(keyPath: "transform.rotation.z") @@ -599,7 +550,6 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate animation.repeatCount = .infinity layer.add(animation, forKey: "transform.rotation.z") } - self.setupRecorder(delta: 0.033, duration: 1.2) case .zoomIn: let animation = CABasicAnimation(keyPath: "transform.scale") animation.fromValue = 0.001 as NSNumber @@ -766,7 +716,7 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate } func getRenderAnimationFrames() -> [DrawingTextEntity.AnimationFrame]? { - return self.cachedFrames + return nil } } diff --git a/submodules/DrawingUI/Sources/DrawingVectorEntity.swift b/submodules/DrawingUI/Sources/DrawingVectorEntityView.swift similarity index 100% rename from submodules/DrawingUI/Sources/DrawingVectorEntity.swift rename to submodules/DrawingUI/Sources/DrawingVectorEntityView.swift diff --git a/submodules/DrawingUI/Sources/StickerPickerScreen.swift b/submodules/DrawingUI/Sources/StickerPickerScreen.swift index ba18ea5bf9d..52f4a47a76c 100644 --- a/submodules/DrawingUI/Sources/StickerPickerScreen.swift +++ b/submodules/DrawingUI/Sources/StickerPickerScreen.swift @@ -538,6 +538,9 @@ public class StickerPickerScreen: ViewController { self.storyStickersContentView?.reactionAction = { [weak self] in self?.controller?.addReaction() } + self.storyStickersContentView?.cameraAction = { [weak self] in + self?.controller?.addCamera() + } let gifItems: Signal if controller.hasGifs { @@ -1258,6 +1261,7 @@ public class StickerPickerScreen: ViewController { guard let self, let controller = self.controller, let file = item.itemFile else { return } + let presentationData = controller.context.sharedContext.currentPresentationData.with { $0 } if groupId == AnyHashable("featuredTop") { let viewKey = PostboxViewKey.orderedItemList(id: Namespaces.OrderedItemList.CloudFeaturedStickerPacks) let _ = (controller.context.account.postbox.combinedView(keys: [viewKey]) @@ -1268,10 +1272,11 @@ public class StickerPickerScreen: ViewController { } for featuredStickerPack in view.items.lazy.map({ $0.contents.get(FeaturedStickerPackItem.self)! }) { if featuredStickerPack.topItems.contains(where: { $0.file.fileId == file.fileId }) { - controller.push(FeaturedStickersScreen( + controller.pushController(FeaturedStickersScreen( context: controller.context, highlightedPackId: featuredStickerPack.info.id, - forceTheme: defaultDarkPresentationTheme, + forceTheme: defaultDarkColorPresentationTheme, + stickerActionTitle: presentationData.strings.StickerPack_AddSticker, sendSticker: { [weak self] fileReference, _, _ in guard let self, let controller = self.controller else { return false @@ -1964,6 +1969,7 @@ public class StickerPickerScreen: ViewController { public var presentLocationPicker: () -> Void = { } public var presentAudioPicker: () -> Void = { } public var addReaction: () -> Void = { } + public var addCamera: () -> Void = { } public init(context: AccountContext, inputData: Signal, defaultToEmoji: Bool = false, hasGifs: Bool = false) { self.context = context @@ -2089,13 +2095,6 @@ private final class InteractiveStickerButtonContent: Component { } func update(component: InteractiveStickerButtonContent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { -// if component.useOpaqueTheme { -// self.backgroundLayer.backgroundColor = component.theme.chat.inputMediaPanel.panelContentControlOpaqueSelectionColor.cgColor -// self.tintBackgroundLayer.backgroundColor = UIColor.white.cgColor -// } else { -// self.backgroundLayer.backgroundColor = component.theme.chat.inputMediaPanel.panelContentControlVibrantSelectionColor.cgColor -// self.tintBackgroundLayer.backgroundColor = UIColor(white: 1.0, alpha: 0.2).cgColor -// } self.backgroundLayer.backgroundColor = UIColor(rgb: 0xffffff, alpha: 0.11).cgColor let iconSize = self.icon.update( @@ -2199,14 +2198,7 @@ private final class InteractiveReactionButtonContent: Component { } func update(component: InteractiveReactionButtonContent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { -// if component.useOpaqueTheme { -// self.backgroundLayer.backgroundColor = component.theme.chat.inputMediaPanel.panelContentControlOpaqueSelectionColor.cgColor -// self.tintBackgroundLayer.backgroundColor = UIColor.white.cgColor -// } else { -// self.backgroundLayer.backgroundColor = component.theme.chat.inputMediaPanel.panelContentControlVibrantSelectionColor.cgColor -// self.tintBackgroundLayer.backgroundColor = UIColor(white: 1.0, alpha: 0.2).cgColor -// } - + let bounds = CGRect(origin: .zero, size: CGSize(width: 54.0, height: 54.0)) let iconSize = self.icon.update( transition: .immediate, component: AnyComponent(BundleIconComponent( @@ -2222,10 +2214,89 @@ private final class InteractiveReactionButtonContent: Component { if view.superview == nil { self.addSubview(view) } - transition.setFrame(view: view, frame: CGRect(origin: .zero, size: iconSize)) + transition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: 2.0, y: 0.0), size: iconSize)) + } + + return bounds.size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +private final class RoundVideoButtonContent: Component { + let theme: PresentationTheme + + public init( + theme: PresentationTheme + ) { + self.theme = theme + } + + public static func ==(lhs: RoundVideoButtonContent, rhs: RoundVideoButtonContent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + return true + } + + final class View: UIView { + override public static var layerClass: AnyClass { + return PassthroughLayer.self + } + + private let backgroundLayer = SimpleLayer() + private var icon: ComponentView + + private var component: InteractiveReactionButtonContent? + + override init(frame: CGRect) { + self.icon = ComponentView() + + super.init(frame: frame) + + self.isExclusiveTouch = true + + self.layer.addSublayer(self.backgroundLayer) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: RoundVideoButtonContent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + self.backgroundLayer.backgroundColor = UIColor(rgb: 0xffffff, alpha: 0.11).cgColor + + let bounds = CGRect(origin: .zero, size: CGSize(width: 54.0, height: 54.0)) + let backgroundSize = CGSize(width: 50.0, height: 50.0) + self.backgroundLayer.frame = backgroundSize.centered(in: bounds) + self.backgroundLayer.cornerRadius = backgroundSize.width / 2.0 + + let iconSize = self.icon.update( + transition: .immediate, + component: AnyComponent(BundleIconComponent( + name: "Chat List/Tabs/IconCamera", + tintColor: nil, + maxSize: CGSize(width: 30.0, height: 30.0) + )), + environment: {}, + containerSize: availableSize + ) + + if let view = self.icon.view { + if view.superview == nil { + self.addSubview(view) + } + transition.setFrame(view: view, frame: iconSize.centered(in: bounds)) } - return iconSize + return bounds.size } } @@ -2357,7 +2428,8 @@ final class StoryStickersContentView: UIView, EmojiCustomContentView { var locationAction: () -> Void = {} var audioAction: () -> Void = {} var reactionAction: () -> Void = {} - + var cameraAction: () -> Void = {} + func update(theme: PresentationTheme, strings: PresentationStrings, useOpaqueTheme: Bool, availableSize: CGSize, transition: Transition) -> CGSize { let padding: CGFloat = 22.0 let size = self.container.update( diff --git a/submodules/DrawingUI/Sources/VideoRecorder.swift b/submodules/DrawingUI/Sources/VideoRecorder.swift new file mode 100644 index 00000000000..cc117612a51 --- /dev/null +++ b/submodules/DrawingUI/Sources/VideoRecorder.swift @@ -0,0 +1,229 @@ +import Foundation +import UIKit +import SwiftSignalKit +import Camera +import MediaEditor +import AVFoundation + +public final class EntityVideoRecorder { + private weak var mediaEditor: MediaEditor? + private weak var entitiesView: DrawingEntitiesView? + + private let maxDuration: Double + + private let camera: Camera + private let previewView: CameraSimplePreviewView + private let entity: DrawingStickerEntity + private weak var entityView: DrawingStickerEntityView? + + private var recordingDisposable = MetaDisposable() + private let durationPromise = ValuePromise() + private let micLevelPromise = Promise() + + private var changingPositionDisposable: Disposable? + + public var duration: Signal { + return self.durationPromise.get() + } + + public var micLevel: Signal { + return self.micLevelPromise.get() + } + + public var onAutomaticStop: () -> Void = {} + + public init(mediaEditor: MediaEditor, entitiesView: DrawingEntitiesView) { + self.mediaEditor = mediaEditor + self.entitiesView = entitiesView + + self.maxDuration = min(60.0, mediaEditor.duration ?? 60.0) + self.previewView = CameraSimplePreviewView(frame: .zero, main: true) + + self.entity = DrawingStickerEntity(content: .dualVideoReference(true)) + + var preferLowerFramerate = false + if let mainFramerate = mediaEditor.mainFramerate { + let frameRate = Int(round(mainFramerate / 30.0) * 30.0) + if frameRate == 30 { + preferLowerFramerate = true + } + } + + self.camera = Camera( + configuration: Camera.Configuration( + preset: .hd1920x1080, + position: .front, + isDualEnabled: false, + audio: true, + photo: false, + metadata: false, + preferWide: true, + preferLowerFramerate: preferLowerFramerate, + reportAudioLevel: true + ), + previewView: self.previewView, + secondaryPreviewView: nil + ) + self.camera.startCapture() + + let action = { [weak self] in + self?.previewView.removePlaceholder(delay: 0.15) + Queue.mainQueue().after(0.1) { + self?.startRecording() + } + } + if #available(iOS 13.0, *) { + let _ = (self.previewView.isPreviewing + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { _ in + action() + }) + } else { + Queue.mainQueue().after(0.35) { + action() + } + } + + self.micLevelPromise.set(camera.audioLevel) + + let start = mediaEditor.values.videoTrimRange?.lowerBound ?? 0.0 + mediaEditor.stop() + mediaEditor.seek(start, andPlay: false) + + self.changingPositionDisposable = (camera.modeChange + |> deliverOnMainQueue).start(next: { [weak self] modeChange in + guard let self else { + return + } + if case .position = modeChange { + self.entityView?.beginCameraSwitch() + } else { + self.entityView?.commitCameraSwitch() + } + }) + } + + deinit { + self.recordingDisposable.dispose() + self.changingPositionDisposable?.dispose() + } + + public func setup( + referenceDrawingSize: CGSize, + scale: CGFloat, + position: CGPoint + ) { + self.entity.referenceDrawingSize = referenceDrawingSize + self.entity.scale = scale + self.entity.position = position + self.entitiesView?.add(self.entity) + + if let entityView = self.entitiesView?.getView(for: self.entity.uuid) as? DrawingStickerEntityView { + let maxDuration = self.maxDuration + entityView.setupCameraPreviewView( + self.previewView, + progress: self.durationPromise.get() |> map { + Float(max(0.0, min(1.0, $0 / maxDuration))) + } + ) + self.previewView.resetPlaceholder(front: true) + entityView.animateInsertion() + + self.entityView = entityView + } + + self.entitiesView?.selectEntity(nil) + } + + var start: Double = 0.0 + private func startRecording() { + guard let mediaEditor = self.mediaEditor else { + self.onAutomaticStop() + return + } + mediaEditor.maybeMuteVideo() + mediaEditor.play() + + self.start = CACurrentMediaTime() + self.recordingDisposable.set((self.camera.startRecording() + |> deliverOnMainQueue).startStrict(next: { [weak self] duration in + guard let self else { + return + } + self.durationPromise.set(duration) + if duration >= self.maxDuration { + let onAutomaticStop = self.onAutomaticStop + self.stopRecording(save: true, completion: { + onAutomaticStop() + }) + } + })) + } + + public func stopRecording(save: Bool, completion: @escaping () -> Void = {}) { + var save = save + let duration = CACurrentMediaTime() - self.start + if duration < 0.2 { + save = false + } + self.recordingDisposable.set((self.camera.stopRecording() + |> deliverOnMainQueue).startStrict(next: { [weak self] result in + guard let self, let mediaEditor = self.mediaEditor, let entitiesView = self.entitiesView, case let .finished(mainResult, _, _, _, _) = result else { + return + } + if save { + let duration = AVURLAsset(url: URL(fileURLWithPath: mainResult.path)).duration + + let start = mediaEditor.values.videoTrimRange?.lowerBound ?? 0.0 + mediaEditor.setAdditionalVideoOffset(-start, apply: false) + mediaEditor.setAdditionalVideoTrimRange(0 ..< duration.seconds, apply: true) + mediaEditor.setAdditionalVideo(mainResult.path, positionChanges: []) + + mediaEditor.stop() + Queue.mainQueue().justDispatch { + mediaEditor.seek(start, andPlay: true) + } + + if let entityView = entitiesView.getView(for: self.entity.uuid) as? DrawingStickerEntityView { + entityView.snapshotCameraPreviewView() + + mediaEditor.setOnNextAdditionalDisplay { [weak entityView] in + Queue.mainQueue().async { + entityView?.invalidateCameraPreviewView() + } + } + + let entity = self.entity + let update = { [weak mediaEditor, weak entity] in + if let mediaEditor, let entity { + mediaEditor.setAdditionalVideoPosition(entity.position, scale: entity.scale, rotation: entity.rotation) + } + } + entityView.updated = { + update() + } + update() + } + + self.camera.stopCapture(invalidate: true) + self.mediaEditor?.maybeUnmuteVideo() + completion() + } + })) + + if !save { + self.camera.stopCapture(invalidate: true) + self.mediaEditor?.maybeUnmuteVideo() + + self.entitiesView?.remove(uuid: self.entity.uuid, animated: true) + self.mediaEditor?.setAdditionalVideo(nil, positionChanges: []) + + completion() + } + } + + public func togglePosition() { + self.camera.togglePosition() + } +} diff --git a/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift b/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift index 39b428ba5ea..3a256878765 100644 --- a/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift +++ b/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift @@ -319,7 +319,7 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode { if let strongSelf = self, let info = info as? StickerPackCollectionInfo { strongSelf.view.window?.endEditing(true) let packReference: StickerPackReference = .id(id: info.id.id, accessHash: info.accessHash) - let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: strongSelf.controller?.navigationController as? NavigationController, sendSticker: { fileReference, sourceNode, sourceRect in + let controller = StickerPackScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], actionTitle: strongSelf.controller?.stickerActionTitle, parentNavigationController: strongSelf.controller?.navigationController as? NavigationController, sendSticker: { fileReference, sourceNode, sourceRect in if let strongSelf = self { return strongSelf.sendSticker?(fileReference, sourceNode, sourceRect) ?? false } else { @@ -442,6 +442,15 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode { self.loadMoreDisposable.dispose() } + var updatedPresentationData: (initial: PresentationData, signal: Signal)? { + if let forceTheme = self.controller?.forceTheme { + let presentationData = self.presentationData.withUpdated(theme: forceTheme) + return (presentationData, .single(presentationData)) + } else { + return nil + } + } + func updatePresentationData(presentationData: PresentationData) { self.presentationData = presentationData @@ -543,7 +552,7 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode { switch attribute { case let .Sticker(_, packReference, _): if let packReference = packReference { - let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: strongSelf.controller?.navigationController as? NavigationController, sendSticker: { file, sourceNode, sourceRect in + let controller = StickerPackScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], actionTitle: strongSelf.controller?.stickerActionTitle, parentNavigationController: strongSelf.controller?.navigationController as? NavigationController, sendSticker: { file, sourceNode, sourceRect in if let strongSelf = self { return strongSelf.sendSticker?(file, sourceNode, sourceRect) ?? false } else { @@ -631,7 +640,7 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode { switch attribute { case let .Sticker(_, packReference, _): if let packReference = packReference { - let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: strongSelf.controller?.navigationController as? NavigationController, sendSticker: { file, sourceNode, sourceRect in + let controller = StickerPackScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], actionTitle: strongSelf.controller?.stickerActionTitle, parentNavigationController: strongSelf.controller?.navigationController as? NavigationController, sendSticker: { file, sourceNode, sourceRect in if let strongSelf = self { return strongSelf.sendSticker?(file, sourceNode, sourceRect) ?? false } else { @@ -804,6 +813,7 @@ public final class FeaturedStickersScreen: ViewController { private let context: AccountContext fileprivate let highlightedPackId: ItemCollectionId? private let sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? + fileprivate var stickerActionTitle: String? private var controllerNode: FeaturedStickersScreenNode { return self.displayNode as! FeaturedStickersScreenNode @@ -820,10 +830,11 @@ public final class FeaturedStickersScreen: ViewController { fileprivate var searchNavigationNode: SearchNavigationContentNode? - public init(context: AccountContext, highlightedPackId: ItemCollectionId?, forceTheme: PresentationTheme? = nil, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? = nil) { + public init(context: AccountContext, highlightedPackId: ItemCollectionId?, forceTheme: PresentationTheme? = nil, stickerActionTitle: String? = nil, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? = nil) { self.context = context self.highlightedPackId = highlightedPackId self.sendSticker = sendSticker + self.stickerActionTitle = stickerActionTitle var presentationData = context.sharedContext.currentPresentationData.with { $0 } if let forceTheme { @@ -1181,7 +1192,7 @@ private final class FeaturedPaneSearchContentNode: ASDisplayNode { if let strongSelf = self { strongSelf.view.window?.endEditing(true) let packReference: StickerPackReference = .id(id: info.id.id, accessHash: info.accessHash) - let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: strongSelf.controller?.navigationController as? NavigationController, sendSticker: { [weak self] fileReference, sourceNode, sourceRect in + let controller = StickerPackScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], actionTitle: strongSelf.controller?.stickerActionTitle, parentNavigationController: strongSelf.controller?.navigationController as? NavigationController, sendSticker: { [weak self] fileReference, sourceNode, sourceRect in if let strongSelf = self { return strongSelf.sendSticker?(fileReference, sourceNode, sourceRect) ?? false } else { @@ -1219,6 +1230,11 @@ private final class FeaturedPaneSearchContentNode: ASDisplayNode { self.installDisposable.dispose() } + var updatedPresentationData: (initial: PresentationData, signal: Signal)? { + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: self.theme) + return (presentationData, .single(presentationData)) + } + func updateText(_ text: String, languageCode: String?) { let signal: Signal<([(String?, FoundStickerItem)], FoundStickerSets, Bool, FoundStickerSets?)?, NoError> if !text.isEmpty { diff --git a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift index fac4bcfb8fd..6000609303e 100644 --- a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift +++ b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift @@ -176,6 +176,8 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll private var validLayout: (CGSize, LayoutMetrics, CGFloat, CGFloat, CGFloat, CGFloat)? + private var codeHighlightState: (id: EngineMessage.Id, specs: [CachedMessageSyntaxHighlight.Spec], disposable: Disposable)? + var playbackControl: (() -> Void)? var seekBackward: ((Double) -> Void)? var seekForward: ((Double) -> Void)? @@ -634,6 +636,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll deinit { self.messageContextDisposable.dispose() + self.codeHighlightState?.disposable.dispose() } override func didLoad() { @@ -953,7 +956,39 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll } } } - messageText = galleryCaptionStringWithAppliedEntities(text, entities: entities, message: message) + + let codeHighlightSpecs = extractMessageSyntaxHighlightSpecs(text: text, entities: entities) + var cachedMessageSyntaxHighlight: CachedMessageSyntaxHighlight? + + if !codeHighlightSpecs.isEmpty { + for attribute in message.attributes { + if let attribute = attribute as? DerivedDataMessageAttribute { + if let value = attribute.data["code"]?.get(CachedMessageSyntaxHighlight.self) { + cachedMessageSyntaxHighlight = value + } + } + } + } + + if !codeHighlightSpecs.isEmpty { + if let current = self.codeHighlightState, current.id == message.id, current.specs == codeHighlightSpecs { + } else { + if let codeHighlightState = self.codeHighlightState { + self.codeHighlightState = nil + codeHighlightState.disposable.dispose() + } + + let disposable = MetaDisposable() + self.codeHighlightState = (message.id, codeHighlightSpecs, disposable) + disposable.set(asyncUpdateMessageSyntaxHighlight(engine: self.context.engine, messageId: message.id, current: cachedMessageSyntaxHighlight, specs: codeHighlightSpecs).startStrict(completed: { + })) + } + } else if let codeHighlightState = self.codeHighlightState { + self.codeHighlightState = nil + codeHighlightState.disposable.dispose() + } + + messageText = galleryCaptionStringWithAppliedEntities(context: self.context, text: text, entities: entities, message: message, cachedMessageSyntaxHighlight: cachedMessageSyntaxHighlight) } if self.currentMessageText != messageText || canDelete != !self.deleteButton.isHidden || canFullscreen != !self.fullscreenButton.isHidden || canShare != !self.actionButton.isHidden || canEdit != !self.editButton.isHidden || self.currentAuthorNameText != authorNameText || self.currentDateText != dateText { diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index 0a372dca8cf..9e9b7b7d7cb 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -138,8 +138,31 @@ private let italicFont = Font.italic(16.0) private let boldItalicFont = Font.semiboldItalic(16.0) private let fixedFont = UIFont(name: "Menlo-Regular", size: 15.0) ?? textFont -public func galleryCaptionStringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], message: Message?) -> NSAttributedString { - return stringWithAppliedEntities(text, entities: entities, baseColor: .white, linkColor: UIColor(rgb: 0x5ac8fa), baseFont: textFont, linkFont: textFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: fixedFont, blockQuoteFont: textFont, underlineLinks: false, message: message, adjustQuoteFontSize: true) +public func galleryCaptionStringWithAppliedEntities(context: AccountContext, text: String, entities: [MessageTextEntity], message: Message?, cachedMessageSyntaxHighlight: CachedMessageSyntaxHighlight? = nil) -> NSAttributedString { + var baseQuoteSecondaryTintColor: UIColor? + var baseQuoteTertiaryTintColor: UIColor? + if let nameColor = message?.author?.nameColor { + let resolvedColor = context.peerNameColors.get(nameColor) + if resolvedColor.secondary != nil { + baseQuoteSecondaryTintColor = .clear + } + if resolvedColor.tertiary != nil { + baseQuoteTertiaryTintColor = .clear + } + } + + return stringWithAppliedEntities( + text, + entities: entities, + baseColor: .white, + linkColor: UIColor(rgb: 0x5ac8fa), + baseQuoteTintColor: .white, + baseQuoteSecondaryTintColor: baseQuoteSecondaryTintColor, + baseQuoteTertiaryTintColor: baseQuoteTertiaryTintColor, + codeBlockTitleColor: .white, + codeBlockAccentColor: .white, + codeBlockBackgroundColor: UIColor(white: 1.0, alpha: 0.2), + baseFont: textFont, linkFont: textFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: fixedFont, blockQuoteFont: textFont, underlineLinks: false, message: message, adjustQuoteFontSize: true, cachedMessageSyntaxHighlight: cachedMessageSyntaxHighlight) } private func galleryMessageCaptionText(_ message: Message) -> String { @@ -228,7 +251,7 @@ public func galleryItemForEntry( entities = result } - let caption = galleryCaptionStringWithAppliedEntities(text, entities: entities, message: message) + let caption = galleryCaptionStringWithAppliedEntities(context: context, text: text, entities: entities, message: message) return UniversalVideoGalleryItem( context: context, presentationData: presentationData, @@ -325,7 +348,7 @@ public func galleryItemForEntry( if let result = addLocallyGeneratedEntities(descriptionText, enabledTypes: [.timecode], entities: entities, mediaDuration: 86400) { entities = result } - description = galleryCaptionStringWithAppliedEntities(descriptionText, entities: entities, message: message) + description = galleryCaptionStringWithAppliedEntities(context: context, text: descriptionText, entities: entities, message: message) } return UniversalVideoGalleryItem( context: context, diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index 5f837beadf4..03e7d7c4ff6 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -336,7 +336,7 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { self.translateToLanguage = translateToLanguage self.peerIsCopyProtected = peerIsCopyProtected self.isSecret = isSecret - self.imageNode.captureProtected = message.isCopyProtected() || peerIsCopyProtected || isSecret + self.imageNode.captureProtected = message.id.peerId.namespace == Namespaces.Peer.SecretChat || message.isCopyProtected() || peerIsCopyProtected || isSecret self.footerContentNode.setMessage(message, displayInfo: displayInfo, translateToLanguage: translateToLanguage, peerIsCopyProtected: peerIsCopyProtected) } diff --git a/submodules/GraphCore/Sources/Helpers/TimeZone.swift b/submodules/GraphCore/Sources/Helpers/TimeZone.swift index 457ef9aec1d..ff4b9038ce6 100644 --- a/submodules/GraphCore/Sources/Helpers/TimeZone.swift +++ b/submodules/GraphCore/Sources/Helpers/TimeZone.swift @@ -35,7 +35,7 @@ extension DateFormatter { let formatter = DateFormatter() formatter.calendar = Calendar.utc formatter.dateFormat = format - formatter.timeZone = TimeZone.utc + formatter.timeZone = .current return formatter } } diff --git a/submodules/GraphUI/Sources/ChartNode.swift b/submodules/GraphUI/Sources/ChartNode.swift index c461832c06b..2bb978f416d 100644 --- a/submodules/GraphUI/Sources/ChartNode.swift +++ b/submodules/GraphUI/Sources/ChartNode.swift @@ -147,12 +147,16 @@ public final class ChartNode: ASDisplayNode { self.chartView.apply(theme: theme, strings: strings, animated: false) } - public func setup(controller: BaseChartController) { + public func setup(controller: BaseChartController, noInitialZoom: Bool = false) { var displayRange = true + var zoomToEnding = true if let controller = controller as? StepBarsChartController { displayRange = !controller.hourly } - self.chartView.setup(controller: controller, displayRange: displayRange) + if noInitialZoom { + zoomToEnding = false + } + self.chartView.setup(controller: controller, displayRange: displayRange, zoomToEnding: zoomToEnding) } public func resetInteraction() { diff --git a/submodules/GraphUI/Sources/ChartStackSection.swift b/submodules/GraphUI/Sources/ChartStackSection.swift index 7e35df4bcc6..d0c5f41cc33 100644 --- a/submodules/GraphUI/Sources/ChartStackSection.swift +++ b/submodules/GraphUI/Sources/ChartStackSection.swift @@ -181,7 +181,7 @@ class ChartStackSection: UIView, ChartThemeContainer { self.chartView.setNeedsDisplay() } - func setup(controller: BaseChartController, displayRange: Bool = true) { + func setup(controller: BaseChartController, displayRange: Bool = true, zoomToEnding: Bool = true) { self.controller = controller self.displayRange = displayRange @@ -246,7 +246,7 @@ class ChartStackSection: UIView, ChartThemeContainer { controller.initializeChart() updateToolViews(animated: false) - let range: ClosedRange = displayRange ? 0.8 ... 1.0 : 0.0 ... 1.0 + let range: ClosedRange = displayRange && zoomToEnding ? 0.8 ... 1.0 : 0.0 ... 1.0 rangeView.setRange(range, animated: false) controller.updateChartRange(range, animated: false) diff --git a/submodules/GraphUI/Sources/ChartVisibilityItemView.swift b/submodules/GraphUI/Sources/ChartVisibilityItemView.swift index 3b6025e0c02..2ededc52656 100644 --- a/submodules/GraphUI/Sources/ChartVisibilityItemView.swift +++ b/submodules/GraphUI/Sources/ChartVisibilityItemView.swift @@ -32,7 +32,7 @@ class ChartVisibilityItemView: UIView { func setupView() { checkButton.frame = bounds checkButton.titleLabel?.font = ChartVisibilityItemView.textFont - checkButton.layer.cornerRadius = 6 + checkButton.layer.cornerRadius = 15 checkButton.layer.masksToBounds = true checkButton.addTarget(self, action: #selector(didTapButton), for: .touchUpInside) let pressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(didRecognizedLongPress(recognizer:))) diff --git a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift index c2e3048b8a8..5f19df40943 100644 --- a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift @@ -1369,7 +1369,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { self?.present(c, a) }, dismissInput: { self?.view.endEditing(true) - }, contentContext: nil, progress: nil) + }, contentContext: nil, progress: nil, completion: nil) } } })) @@ -1412,7 +1412,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { }, openUrl: { _ in }, openPeer: { _ in }, showAll: false) - let peer = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil) + let peer = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer, text: "", attributes: [], media: [map], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) let controller = LocationViewController(context: self.context, subject: EngineMessage(message), params: controllerParams) diff --git a/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift b/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift index 0146ab8af08..ba8fe938f5c 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift @@ -726,7 +726,7 @@ public final class InviteLinkViewController: ViewController { if requestsState.importers.isEmpty && requestsState.isLoadingMore { count = min(4, state.count) loading = true - let fakeUser = TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil) + let fakeUser = TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) for i in 0 ..< count { entries.append(.request(Int32(i), presentationData.theme, presentationData.dateTimeFormat, EnginePeer.user(fakeUser), 0, true)) } @@ -760,7 +760,7 @@ public final class InviteLinkViewController: ViewController { if state.importers.isEmpty && state.isLoadingMore { count = min(4, state.count) loading = true - let fakeUser = TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil) + let fakeUser = TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) for i in 0 ..< count { entries.append(.importer(Int32(i), presentationData.theme, presentationData.dateTimeFormat, EnginePeer.user(fakeUser), 0, false, true)) } diff --git a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift index 4d8cebf90db..093c11e33ed 100644 --- a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift +++ b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift @@ -468,6 +468,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem { let hasTopGroupInset: Bool let noInsets: Bool let noCorners: Bool + let style: ItemListStyle public let tag: ItemListItemTag? let header: ListViewItemHeader? let shimmering: ItemListPeerItemShimmering? @@ -508,6 +509,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem { hasTopGroupInset: Bool = true, noInsets: Bool = false, noCorners: Bool = false, + style: ItemListStyle = .blocks, tag: ItemListItemTag? = nil, header: ListViewItemHeader? = nil, shimmering: ItemListPeerItemShimmering? = nil, @@ -547,6 +549,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem { self.hasTopGroupInset = hasTopGroupInset self.noInsets = noInsets self.noCorners = noCorners + self.style = style self.tag = tag self.header = header self.shimmering = shimmering @@ -588,6 +591,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem { hasTopGroupInset: Bool = true, noInsets: Bool = false, noCorners: Bool = false, + style: ItemListStyle = .blocks, tag: ItemListItemTag? = nil, header: ListViewItemHeader? = nil, shimmering: ItemListPeerItemShimmering? = nil, @@ -627,6 +631,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem { self.hasTopGroupInset = hasTopGroupInset self.noInsets = noInsets self.noCorners = noCorners + self.style = style self.tag = tag self.header = header self.shimmering = shimmering @@ -889,7 +894,6 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo return { item, params, neighbors, headerAtTop in var updateArrowImage: UIImage? - var updatedTheme: PresentationTheme? let statusFontSize: CGFloat = floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0) let labelFontSize: CGFloat = floor(item.presentationData.fontSize.itemListBaseFontSize * 13.0 / 17.0) @@ -938,7 +942,6 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo let badgeDiameter: CGFloat = 20.0 if currentItem?.presentationData.theme !== item.presentationData.theme { - updatedTheme = item.presentationData.theme updateArrowImage = PresentationResourcesItemList.disclosureArrowImage(item.presentationData.theme) if let badgeColor = badgeColor { updatedLabelBadgeImage = generateStretchableFilledCircleImage(diameter: badgeDiameter, color: badgeColor) @@ -1247,13 +1250,22 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo strongSelf.labelArrowNode?.image = updateArrowImage } - if let _ = updatedTheme { - strongSelf.topStripeNode.backgroundColor = item.presentationData.theme.list.itemBlocksSeparatorColor - strongSelf.bottomStripeNode.backgroundColor = item.presentationData.theme.list.itemBlocksSeparatorColor - strongSelf.backgroundNode.backgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor - strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor + let itemBackgroundColor: UIColor + let itemSeparatorColor: UIColor + switch item.style { + case .plain: + itemBackgroundColor = item.presentationData.theme.list.plainBackgroundColor + itemSeparatorColor = item.presentationData.theme.list.itemPlainSeparatorColor + case .blocks: + itemBackgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor + itemSeparatorColor = item.presentationData.theme.list.itemBlocksSeparatorColor } + strongSelf.topStripeNode.backgroundColor = itemSeparatorColor + strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor + strongSelf.backgroundNode.backgroundColor = itemBackgroundColor + strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor + let revealOffset = strongSelf.revealOffset let transition: ContainedViewLayoutTransition diff --git a/submodules/ItemListUI/BUILD b/submodules/ItemListUI/BUILD index 59b5c920320..1791a5db3c7 100644 --- a/submodules/ItemListUI/BUILD +++ b/submodules/ItemListUI/BUILD @@ -29,6 +29,8 @@ swift_library( "//submodules/ManagedAnimationNode:ManagedAnimationNode", "//submodules/AvatarNode", "//submodules/TelegramCore", + "//submodules/ComponentFlow", + "//submodules/TelegramUI/Components/TabSelectorComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/ItemListUI/Sources/ItemListControllerSegmentedTitleView.swift b/submodules/ItemListUI/Sources/ItemListControllerSegmentedTitleView.swift index e98741580d2..7da5bebbdd9 100644 --- a/submodules/ItemListUI/Sources/ItemListControllerSegmentedTitleView.swift +++ b/submodules/ItemListUI/Sources/ItemListControllerSegmentedTitleView.swift @@ -2,48 +2,48 @@ import Foundation import UIKit import SegmentedControlNode import TelegramPresentationData +import ComponentFlow +import TabSelectorComponent +import Display public final class ItemListControllerSegmentedTitleView: UIView { - private let segmentedControlNode: SegmentedControlNode + private let tabSelector = ComponentView() public var theme: PresentationTheme { didSet { - self.segmentedControlNode.updateTheme(SegmentedControlTheme(theme: self.theme)) + if self.theme !== oldValue { + self.setNeedsLayout() + } } } public var segments: [String] { didSet { if self.segments != oldValue { - self.segmentedControlNode.items = self.segments.map { SegmentedControlItem(title: $0) } self.setNeedsLayout() } } } public var index: Int { - get { - return self.segmentedControlNode.selectedIndex - } - set { - self.segmentedControlNode.selectedIndex = newValue + didSet { + if self.index != oldValue { + self.animateLayout = true + self.setNeedsLayout() + } } } + private var validLayout: CGSize? + private var animateLayout: Bool = false + public var indexUpdated: ((Int) -> Void)? public init(theme: PresentationTheme, segments: [String], selectedIndex: Int) { self.theme = theme self.segments = segments - - self.segmentedControlNode = SegmentedControlNode(theme: SegmentedControlTheme(theme: theme), items: segments.map { SegmentedControlItem(title: $0) }, selectedIndex: selectedIndex) + self.index = selectedIndex super.init(frame: CGRect()) - - self.segmentedControlNode.selectedIndexChanged = { [weak self] index in - self?.indexUpdated?(index) - } - - self.addSubnode(self.segmentedControlNode) } required public init?(coder aDecoder: NSCoder) { @@ -54,7 +54,57 @@ public final class ItemListControllerSegmentedTitleView: UIView { super.layoutSubviews() let size = self.bounds.size - let controlSize = self.segmentedControlNode.updateLayout(.sizeToFit(maximumWidth: size.width, minimumWidth: 160.0, height: 32.0), transition: .immediate) - self.segmentedControlNode.frame = CGRect(origin: CGPoint(x: floor((size.width - controlSize.width) / 2.0), y: floor((size.height - controlSize.height) / 2.0)), size: controlSize) + self.validLayout = size + self.update(transition: .immediate) + } + + private func update(transition: Transition) { + guard let size = self.validLayout else { + return + } + + let mappedItems = zip(0 ..< self.segments.count, self.segments).map { index, segment in + return TabSelectorComponent.Item( + id: AnyHashable(index), + title: segment + ) + } + + var transition = transition + if self.animateLayout { + transition = .spring(duration: 0.4) + self.animateLayout = false + } + + let tabSelectorSize = self.tabSelector.update( + transition: transition, + component: AnyComponent(TabSelectorComponent( + colors: TabSelectorComponent.Colors( + foreground: self.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.8), + selection: self.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.05) + ), + customLayout: TabSelectorComponent.CustomLayout( + font: Font.medium(15.0), + spacing: 8.0 + ), + items: mappedItems, + selectedId: AnyHashable(self.index), + setSelectedId: { [weak self] id in + guard let self, let index = id.base as? Int else { + return + } + self.indexUpdated?(index) + } + )), + 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)), size: tabSelectorSize) + if let tabSelectorView = self.tabSelector.view { + if tabSelectorView.superview == nil { + self.addSubview(tabSelectorView) + } + transition.setFrame(view: tabSelectorView, frame: tabSelectorFrame) + } } } diff --git a/submodules/ItemListUI/Sources/ItemListItem.swift b/submodules/ItemListUI/Sources/ItemListItem.swift index ca0b6987a4e..7cede4afcb4 100644 --- a/submodules/ItemListUI/Sources/ItemListItem.swift +++ b/submodules/ItemListUI/Sources/ItemListItem.swift @@ -161,12 +161,14 @@ public final class ItemListPresentationData: Equatable { public let fontSize: PresentationFontSize public let strings: PresentationStrings public let nameDisplayOrder: PresentationPersonNameOrder + public let dateTimeFormat: PresentationDateTimeFormat - public init(theme: PresentationTheme, fontSize: PresentationFontSize, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder) { + public init(theme: PresentationTheme, fontSize: PresentationFontSize, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat) { self.theme = theme self.fontSize = fontSize self.strings = strings self.nameDisplayOrder = nameDisplayOrder + self.dateTimeFormat = dateTimeFormat } public static func ==(lhs: ItemListPresentationData, rhs: ItemListPresentationData) -> Bool { @@ -182,6 +184,9 @@ public final class ItemListPresentationData: Equatable { if lhs.nameDisplayOrder != rhs.nameDisplayOrder { return false } + if lhs.dateTimeFormat != rhs.dateTimeFormat { + return false + } return true } } @@ -232,6 +237,6 @@ public extension PresentationFontSize { public extension ItemListPresentationData { convenience init(_ presentationData: PresentationData) { - self.init(theme: presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder) + self.init(theme: presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat) } } diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernConversationInputMicButton.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernConversationInputMicButton.h index 9e94a8da143..4ea98cdf5f7 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernConversationInputMicButton.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernConversationInputMicButton.h @@ -87,4 +87,6 @@ - (void)_commitLocked; +- (void)setHidesPanelOnLock; + @end diff --git a/submodules/LegacyComponents/Sources/TGModernConversationInputMicButton.m b/submodules/LegacyComponents/Sources/TGModernConversationInputMicButton.m index b2ff07f07d3..bf73ed470e5 100644 --- a/submodules/LegacyComponents/Sources/TGModernConversationInputMicButton.m +++ b/submodules/LegacyComponents/Sources/TGModernConversationInputMicButton.m @@ -135,6 +135,8 @@ @interface TGModernConversationInputMicButton () CGFloat _inputLevel; bool _animatedIn; + bool _hidesPanelOnLock; + UIImage *_icon; id _presentation; @@ -557,6 +559,31 @@ - (void)dismiss _presentation = nil; } +- (void)setHidesPanelOnLock { + _hidesPanelOnLock = true; +} + ++ (UIImage *)stopIconImage +{ + static dispatch_once_t onceToken; + static UIImage *iconImage; + dispatch_once(&onceToken, ^ + { + CGRect rect = CGRectMake(0, 0, 22.0f, 22.0f); + UIGraphicsBeginImageContextWithOptions(rect.size, false, 0); + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGContextAddPath(context, [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 22, 22) cornerRadius:7].CGPath); + CGContextSetFillColorWithColor(context, UIColorRGBA(0x0ffffff, 1.3f).CGColor); + CGContextFillPath(context); + + iconImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + }); + return iconImage; +} + + - (void)animateLock { if (!_animatedIn) { return; @@ -569,8 +596,9 @@ - (void)animateLock { snapshotView.frame = _innerIconView.frame; [_innerIconWrapperView insertSubview:snapshotView atIndex:0]; + UIImage *icon = _hidesPanelOnLock ? [TGModernConversationInputMicButton stopIconImage] : TGComponentsImageNamed(@"RecordSendIcon"); _previousIcon = _innerIconView.image; - [self setIcon:TGTintedImage(TGComponentsImageNamed(@"RecordSendIcon"), _pallete != nil ? _pallete.iconColor : [UIColor whiteColor])]; + [self setIcon:TGTintedImage(icon, _pallete != nil && !_hidesPanelOnLock ? _pallete.iconColor : [UIColor whiteColor])]; _currentScale = 1; _cancelTargetTranslation = 0; @@ -598,6 +626,15 @@ - (void)animateLock { _lock.transform = CGAffineTransformMakeTranslation(0.0f, -16.0f); _lockArrowView.transform = CGAffineTransformMakeTranslation(0.0f, -39.0f); _lockArrowView.alpha = 0.0f; + + if (_hidesPanelOnLock) { + _lockPanelView.transform = CGAffineTransformScale(_lockPanelView.transform, 0.01, 0.01); + _lockPanelView.alpha = 0.0; + } + } completion:^(BOOL finished) { + if (_hidesPanelOnLock) { + [_lockPanelWrapperView removeFromSuperview]; + } }]; if (_lock == nil) { diff --git a/submodules/ManagedAnimationNode/Sources/ManagedAnimationNode.swift b/submodules/ManagedAnimationNode/Sources/ManagedAnimationNode.swift index 2343704a2b1..5c834aa457a 100644 --- a/submodules/ManagedAnimationNode/Sources/ManagedAnimationNode.swift +++ b/submodules/ManagedAnimationNode/Sources/ManagedAnimationNode.swift @@ -144,7 +144,7 @@ public struct ManagedAnimationItem { open class ManagedAnimationNode: ASDisplayNode { public let intrinsicSize: CGSize - private let imageNode: ASImageNode + public let imageNode: ASImageNode private let displayLink: SharedDisplayLinkDriver.Link public var imageUpdated: ((UIImage) -> Void)? @@ -182,7 +182,7 @@ open class ManagedAnimationNode: ASDisplayNode { self.imageNode.frame = CGRect(origin: CGPoint(), size: self.intrinsicSize) var displayLinkUpdate: (() -> Void)? - self.displayLink = SharedDisplayLinkDriver.shared.add { + self.displayLink = SharedDisplayLinkDriver.shared.add { _ in displayLinkUpdate?() } diff --git a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift index 920c8be0c3a..a20d645b159 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift @@ -497,7 +497,7 @@ final class MediaPickerGridItemNode: GridItemNode { self.imageNode.contentMode = .scaleAspectFit Queue.concurrentDefaultQueue().async { let colors = mediaEditorGetGradientColors(from: image) - let gradientImage = mediaEditorGenerateGradientImage(size: CGSize(width: 3.0, height: 128.0), colors: [colors.0, colors.1]) + let gradientImage = mediaEditorGenerateGradientImage(size: CGSize(width: 3.0, height: 128.0), colors: colors.array) Queue.mainQueue().async { self.backgroundNode.image = gradientImage } diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index 53cdd3139f8..bd6ed8159e3 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -1865,6 +1865,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { guard let self else { return } + let items = items.filter { $0.count > 0 } var dismissImpl: (() -> Void)? let content: ContextControllerItemsContent = MediaGroupsContextMenuContent( context: self.context, diff --git a/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift b/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift index 2a36c5036aa..390142c661e 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift @@ -472,7 +472,6 @@ private class MessageBackgroundNode: ASDisplayNode { private var absoluteRect: (CGRect, CGSize)? func update(size: CGSize, theme: PresentationTheme, wallpaper: TelegramWallpaper, graphics: PrincipalThemeEssentialGraphics, wallpaperBackgroundNode: WallpaperBackgroundNode, transition: ContainedViewLayoutTransition) { - self.backgroundNode.setType(type: .outgoing(.Extracted), highlighted: false, graphics: graphics, maskMode: false, hasWallpaper: wallpaper.hasWallpaper, transition: transition, backgroundNode: wallpaperBackgroundNode) self.backgroundWallpaperNode.setType(type: .outgoing(.Extracted), theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), essentialGraphics: graphics, maskMode: true, backgroundNode: wallpaperBackgroundNode) self.shadowNode.setType(type: .outgoing(.Extracted), hasWallpaper: wallpaper.hasWallpaper, graphics: graphics) @@ -855,15 +854,15 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)) var peers = SimpleDictionary() - peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil) + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) let previewText = groupLayouts.count > 1 ? presentationData.strings.Attachment_MessagesPreview : presentationData.strings.Attachment_MessagePreview let previewMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: previewText, entities: []))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - let previewItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [previewMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, isCentered: true) + let previewItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [previewMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: true) let dragMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: presentationData.strings.Attachment_DragToReorder, entities: []))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - let dragItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [dragMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, isCentered: true) + let dragItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [dragMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: true) let headerItems: [ListViewItem] = [previewItem, dragItem] @@ -1069,7 +1068,7 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI } let inset: CGFloat = insets.left == 70 ? insets.left : 0.0 - self.wallpaperBackgroundNode.update(wallpaper: wallpaper) + self.wallpaperBackgroundNode.update(wallpaper: wallpaper, animated: false) self.wallpaperBackgroundNode.updateBubbleTheme(bubbleTheme: theme, bubbleCorners: bubbleCorners) transition.updateFrame(node: self.wallpaperBackgroundNode, frame: CGRect(origin: CGPoint(x: inset, y: 0.0), size: CGSize(width: size.width - inset * 2.0, height: size.height))) self.wallpaperBackgroundNode.updateLayout(size: CGSize(width: size.width - inset * 2.0, height: size.height), displayMode: .aspectFill, transition: transition) diff --git a/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift b/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift index ea29ba5ef29..025c0939933 100644 --- a/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift +++ b/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift @@ -813,7 +813,7 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode { if needsAnimation { if self.displayLink == nil { - let displayLink = SharedDisplayLinkDriver.shared.add { [weak self] in + let displayLink = SharedDisplayLinkDriver.shared.add { [weak self] _ in self?.updateProgress() } self.displayLink = displayLink diff --git a/submodules/MetalEngine/BUILD b/submodules/MetalEngine/BUILD new file mode 100644 index 00000000000..2460b6126bb --- /dev/null +++ b/submodules/MetalEngine/BUILD @@ -0,0 +1,63 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +load( + "@build_bazel_rules_apple//apple:resources.bzl", + "apple_resource_bundle", + "apple_resource_group", +) +load("//build-system/bazel-utils:plist_fragment.bzl", + "plist_fragment", +) + +filegroup( + name = "MetalSources", + srcs = glob([ + "Metal/**/*.metal", + ]), + visibility = ["//visibility:public"], +) + +plist_fragment( + name = "MetalEngineMetalSourcesBundleInfoPlist", + extension = "plist", + template = + """ + CFBundleIdentifier + org.telegram.MetalEngineMetalSources + CFBundleDevelopmentRegion + en + CFBundleName + MetalEngine + """ +) + +apple_resource_bundle( + name = "MetalEngineMetalSourcesBundle", + infoplists = [ + ":MetalEngineMetalSourcesBundleInfoPlist", + ], + resources = [ + ":MetalSources", + ], +) + +swift_library( + name = "MetalEngine", + module_name = "MetalEngine", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + data = [ + ":MetalEngineMetalSourcesBundle", + ], + deps = [ + "//submodules/Display", + "//submodules/Utils/ShelfPack", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/MetalEngine/Metal/MetalEngineShaders.metal b/submodules/MetalEngine/Metal/MetalEngineShaders.metal new file mode 100644 index 00000000000..0e751f4fa42 --- /dev/null +++ b/submodules/MetalEngine/Metal/MetalEngineShaders.metal @@ -0,0 +1,11 @@ +#include + +using namespace metal; + +vertex float4 clearVertex(const device float2* vertexArray [[ buffer(0) ]], unsigned int vid [[ vertex_id ]]) { + return float4(vertexArray[vid], 0.0, 1.0); +} + +fragment half4 clearFragment(const device float4 &color [[ buffer(0) ]]) { + return half4(color); +} diff --git a/submodules/MetalEngine/Sources/AlignUp.swift b/submodules/MetalEngine/Sources/AlignUp.swift new file mode 100644 index 00000000000..f0a3e5c762e --- /dev/null +++ b/submodules/MetalEngine/Sources/AlignUp.swift @@ -0,0 +1,8 @@ +import Foundation + +func alignUp(_ value: Int, alignment: Int) -> Int { + assert(((alignment - 1) & alignment) == 0) + + let alignmentMask = alignment - 1 + return ((value + alignmentMask) & (~alignmentMask)) +} diff --git a/submodules/MetalEngine/Sources/MetalEngine.swift b/submodules/MetalEngine/Sources/MetalEngine.swift new file mode 100644 index 00000000000..bf25c2ad1db --- /dev/null +++ b/submodules/MetalEngine/Sources/MetalEngine.swift @@ -0,0 +1,1057 @@ +import Foundation +import Metal +import UIKit +import IOSurface +import Display +import ShelfPack + +public final class Placeholder { + var contents: Resolved? +} + +private let noInputPlaceholder: Placeholder = { + let value = Placeholder() + value.contents = Void() + return value +}() + +private struct PlaceholderResolveError: Error { +} + +private func resolvePlaceholder(_ value: Placeholder) throws -> T { + guard let contents = value.contents else { + throw PlaceholderResolveError() + } + return contents +} + +public struct TextureSpec: Equatable { + public enum PixelFormat { + case r8UnsignedNormalized + case rgba8UnsignedNormalized + } + + public var width: Int + public var height: Int + public var pixelFormat: PixelFormat + + public init(width: Int, height: Int, pixelFormat: PixelFormat) { + self.width = width + self.height = height + self.pixelFormat = pixelFormat + } +} + +extension TextureSpec.PixelFormat { + var metalFormat: MTLPixelFormat { + switch self { + case .r8UnsignedNormalized: + return .r8Unorm + case .rgba8UnsignedNormalized: + return .rgba8Unorm + } + } +} + +public final class TexturePlaceholder { + public let placeholer: Placeholder + public let spec: TextureSpec + + init(placeholer: Placeholder, spec: TextureSpec) { + self.placeholer = placeholer + self.spec = spec + } +} + +public struct RenderSize: Equatable { + public var width: Int + public var height: Int + + public init(width: Int, height: Int) { + self.width = width + self.height = height + } +} + +public struct RenderLayerSpec: Equatable { + public var size: RenderSize + public var edgeInset: Int + + public init(size: RenderSize, edgeInset: Int = 0) { + self.size = size + self.edgeInset = edgeInset + } +} + +private extension RenderLayerSpec { + var allocationWidth: Int { + return self.size.width + self.edgeInset * 2 + } + + var allocationHeight: Int { + return self.size.height + self.edgeInset * 2 + } +} + +public struct RenderLayerPlacement: Equatable { + public var effectiveRect: CGRect + + public init(effectiveRect: CGRect) { + self.effectiveRect = effectiveRect + } +} + +public protocol RenderToLayerState: AnyObject { + var pipelineState: MTLRenderPipelineState { get } + + init?(device: MTLDevice) +} + +public protocol ComputeState: AnyObject { + init?(device: MTLDevice) +} + +open class MetalEngineSubjectLayer: SimpleLayer { + fileprivate var internalId: Int = -1 + fileprivate var surfaceAllocation: MetalEngine.SurfaceAllocation? + + public override init() { + super.init() + + self.setNeedsDisplay() + } + + deinit { + MetalEngine.shared.impl.removeLayerSurfaceAllocation(layer: self) + } + + override public init(layer: Any) { + super.init(layer: layer) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override public func setNeedsDisplay() { + if let subject = self as? MetalEngineSubject { + subject.setNeedsUpdate() + } + } +} + +protocol MetalEngineResource: AnyObject { + func free() +} + +public final class PooledTexture { + final class Texture: MetalEngineResource { + let value: MTLTexture + var isInUse: Bool = false + + init(value: MTLTexture) { + self.value = value + } + + public func free() { + self.isInUse = false + } + } + + public let spec: TextureSpec + + private let textures: [Texture] + + init(device: MTLDevice, spec: TextureSpec) { + self.spec = spec + + self.textures = (0 ..< 3).compactMap { _ -> Texture? in + let descriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: spec.pixelFormat.metalFormat, width: spec.width, height: spec.height, mipmapped: false) + descriptor.storageMode = .private + descriptor.usage = [.shaderRead, .shaderWrite] + + guard let texture = device.makeTexture(descriptor: descriptor) else { + return nil + } + return Texture(value: texture) + } + } + + public func get(context: MetalEngineSubjectContext) -> TexturePlaceholder? { + #if DEBUG + if context.freeResourcesOnCompletion.contains(where: { $0 === self }) { + assertionFailure("Trying to get PooledTexture more than once per update cycle") + } + #endif + + for texture in self.textures { + if !texture.isInUse { + texture.isInUse = true + let placeholder = Placeholder() + placeholder.contents = texture.value + context.freeResourcesOnCompletion.append(texture) + return TexturePlaceholder(placeholer: placeholder, spec: self.spec) + } + } + + print("PooledTexture: all textures are in use") + return nil + } +} + +public struct BufferSpec: Equatable { + public var length: Int + + public init(length: Int) { + self.length = length + } +} + +public final class BufferPlaceholder { + public let placeholer: Placeholder + public let spec: BufferSpec + + init(placeholer: Placeholder, spec: BufferSpec) { + self.placeholer = placeholer + self.spec = spec + } +} + +public final class PooledBuffer { + final class Buffer: MetalEngineResource { + let value: MTLBuffer + var isInUse: Bool = false + + init(value: MTLBuffer) { + self.value = value + } + + public func free() { + self.isInUse = false + } + } + + public let spec: BufferSpec + + private let buffers: [Buffer] + + init(device: MTLDevice, spec: BufferSpec) { + self.spec = spec + + self.buffers = (0 ..< 3).compactMap { _ -> Buffer? in + guard let texture = device.makeBuffer(length: spec.length, options: [.storageModePrivate]) else { + return nil + } + return Buffer(value: texture) + } + } + + public func get(context: MetalEngineSubjectContext) -> BufferPlaceholder? { + #if DEBUG + if context.freeResourcesOnCompletion.contains(where: { $0 === self }) { + assertionFailure("Trying to get PooledTexture more than once per update cycle") + } + #endif + + for buffer in self.buffers { + if !buffer.isInUse { + buffer.isInUse = true + let placeholder = Placeholder() + placeholder.contents = buffer.value + context.freeResourcesOnCompletion.append(buffer) + return BufferPlaceholder(placeholer: placeholder, spec: self.spec) + } + } + + print("PooledBuffer: all textures are in use") + return nil + } +} + +public final class SharedBuffer { + public let buffer: MTLBuffer + + init?(device: MTLDevice, spec: BufferSpec) { + guard let buffer = device.makeBuffer(length: spec.length, options: [.storageModeShared]) else { + return nil + } + self.buffer = buffer + } +} + +public final class MetalEngineSubjectContext { + fileprivate final class ComputeOperation { + let commands: (MTLCommandBuffer) -> Void + + init(commands: @escaping (MTLCommandBuffer) -> Void) { + self.commands = commands + } + } + + fileprivate final class RenderToLayerOperation { + let spec: RenderLayerSpec + let state: RenderToLayerState + weak var layer: MetalEngineSubjectLayer? + let commands: (MTLRenderCommandEncoder, RenderLayerPlacement) -> Void + + init( + spec: RenderLayerSpec, + state: RenderToLayerState, + layer: MetalEngineSubjectLayer, + commands: @escaping (MTLRenderCommandEncoder, RenderLayerPlacement) -> Void + ) { + self.spec = spec + self.state = state + self.layer = layer + self.commands = commands + } + } + + private let device: MTLDevice + private let impl: MetalEngine.Impl + + fileprivate var computeOperations: [ComputeOperation] = [] + fileprivate var renderToLayerOperationsGroupedByState: [ObjectIdentifier: [RenderToLayerOperation]] = [:] + fileprivate var freeResourcesOnCompletion: [MetalEngineResource] = [] + + fileprivate init(device: MTLDevice, impl: MetalEngine.Impl) { + self.device = device + self.impl = impl + } + + public func renderToLayer( + spec: RenderLayerSpec, + state: RenderToLayerStateType.Type, + layer: MetalEngineSubjectLayer, + inputs: repeat Placeholder, + commands: @escaping (MTLRenderCommandEncoder, RenderLayerPlacement, repeat each Resolved) -> Void + ) { + let stateTypeId = ObjectIdentifier(state) + let resolvedState: RenderToLayerStateType + if let current = self.impl.renderStates[stateTypeId] as? RenderToLayerStateType { + resolvedState = current + } else { + guard let value = RenderToLayerStateType(device: self.device) else { + assertionFailure("Could not initialize render state \(state)") + return + } + resolvedState = value + self.impl.renderStates[stateTypeId] = resolvedState + } + + let operation = RenderToLayerOperation( + spec: spec, + state: resolvedState, + layer: layer, + commands: { encoder, placement in + let resolvedInputs: (repeat each Resolved) + do { + resolvedInputs = (repeat try resolvePlaceholder(each inputs)) + } catch { + print("Could not resolve renderToLayer inputs") + return + } + commands(encoder, placement, repeat each resolvedInputs) + } + ) + if self.renderToLayerOperationsGroupedByState[stateTypeId] == nil { + self.renderToLayerOperationsGroupedByState[stateTypeId] = [operation] + } else { + self.renderToLayerOperationsGroupedByState[stateTypeId]?.append(operation) + } + } + + public func renderToLayer( + spec: RenderLayerSpec, + state: RenderToLayerStateType.Type, + layer: MetalEngineSubjectLayer, + commands: @escaping (MTLRenderCommandEncoder, RenderLayerPlacement) -> Void + ) { + self.renderToLayer(spec: spec, state: state, layer: layer, inputs: noInputPlaceholder, commands: { encoder, placement, _ in + commands(encoder, placement) + }) + } + + public func compute( + state: ComputeStateType.Type, + inputs: repeat Placeholder, + commands: @escaping (MTLCommandBuffer, ComputeStateType, repeat each Resolved) -> Output + ) -> Placeholder { + let stateTypeId = ObjectIdentifier(state) + let resolvedState: ComputeStateType + if let current = self.impl.computeStates[stateTypeId] as? ComputeStateType { + resolvedState = current + } else { + guard let value = ComputeStateType(device: self.device) else { + assertionFailure("Could not initialize compute state \(state)") + return Placeholder() + } + resolvedState = value + self.impl.computeStates[stateTypeId] = resolvedState + } + + let resultPlaceholder = Placeholder() + self.computeOperations.append(ComputeOperation(commands: { commandBuffer in + let resolvedInputs: (repeat each Resolved) + do { + resolvedInputs = (repeat try resolvePlaceholder(each inputs)) + } catch { + print("Could not resolve renderToLayer inputs") + return + } + resultPlaceholder.contents = commands(commandBuffer, resolvedState, repeat each resolvedInputs) + })) + return resultPlaceholder + } + + public func compute( + state: ComputeStateType.Type, + commands: @escaping (MTLCommandBuffer, ComputeStateType) -> Output + ) -> Placeholder { + return self.compute(state: state, inputs: noInputPlaceholder, commands: { commandBuffer, state, _ in + return commands(commandBuffer, state) + }) + } +} + +public final class MetalEngineSubjectInternalData { + var internalId: Int = -1 + var renderSurfaceAllocation: MetalEngine.SurfaceAllocation? + + init() { + } +} + +public protocol MetalEngineSubject: AnyObject { + var internalData: MetalEngineSubjectInternalData? { get set } + + func setNeedsUpdate() + func update(context: MetalEngineSubjectContext) +} + +public extension MetalEngineSubject { + func setNeedsUpdate() { + MetalEngine.shared.impl.addSubjectNeedsUpdate(subject: self) + } +} + +#if targetEnvironment(simulator) +@available(iOS 13.0, *) +#endif +private final class MetalEventLayer: CAMetalLayer { + var onDisplay: (() -> Void)? + + override func display() { + self.onDisplay?() + } +} + +public final class MetalEngine { + struct SurfaceAllocation { + struct Phase { + let subRect: CGRect + let renderingRect: CGRect + let contentsRect: CGRect + } + + let surfaceId: Int + let allocationId0: Int32 + let allocationId1: Int32 + let renderingParameters: RenderLayerSpec + let phase0: Phase + let phase1: Phase + var currentPhase: Int = 0 + + var effectivePhase: Phase { + if self.currentPhase == 0 { + return self.phase0 + } else { + return self.phase1 + } + } + + init(surfaceId: Int, allocationId0: Int32, allocationId1: Int32, renderingParameters: RenderLayerSpec, phase0: Phase, phase1: Phase) { + self.surfaceId = surfaceId + self.allocationId0 = allocationId0 + self.allocationId1 = allocationId1 + self.renderingParameters = renderingParameters + self.phase0 = phase0 + self.phase1 = phase1 + } + } + + fileprivate final class Surface { + let id: Int + let width: Int + let height: Int + + let ioSurface: IOSurface + let texture: MTLTexture + let packContext: ShelfPackContext + + var isEmpty: Bool { + return self.packContext.isEmpty + } + + init?(id: Int, device: MTLDevice, width: Int, height: Int) { + self.id = id + self.width = width + self.height = height + + self.packContext = ShelfPackContext(width: Int32(width), height: Int32(height)) + + let ioSurfaceProperties: [String: Any] = [ + kIOSurfaceWidth as String: width, + kIOSurfaceHeight as String: height, + kIOSurfaceBytesPerElement as String: 4, + kIOSurfacePixelFormat as String: kCVPixelFormatType_32BGRA + ] + guard let ioSurface = IOSurfaceCreate(ioSurfaceProperties as CFDictionary) else { + return nil + } + self.ioSurface = ioSurface + + let textureDescriptor = MTLTextureDescriptor() + textureDescriptor.pixelFormat = .bgra8Unorm + textureDescriptor.width = Int(width) + textureDescriptor.height = Int(height) + textureDescriptor.storageMode = .shared + textureDescriptor.usage = .renderTarget + + guard let texture = device.makeTexture(descriptor: textureDescriptor, iosurface: ioSurface, plane: 0) else { + return nil + } + self.texture = texture + } + + private struct AllocationLayout { + let subRect: CGRect + let renderingRect: CGRect + let contentsRect: CGRect + + init(baseRect: CGRect, surfaceWidth: Int, surfaceHeight: Int) { + self.subRect = CGRect(origin: CGPoint(x: baseRect.minX, y: baseRect.minY), size: CGSize(width: baseRect.width, height: baseRect.height)) + self.renderingRect = CGRect(origin: CGPoint(x: self.subRect.minX / CGFloat(surfaceWidth), y: self.subRect.minY / CGFloat(surfaceHeight)), size: CGSize(width: self.subRect.width / CGFloat(surfaceWidth), height: self.subRect.height / CGFloat(surfaceHeight))) + self.contentsRect = CGRect(origin: CGPoint(x: self.subRect.minX / CGFloat(surfaceWidth), y: 1.0 - self.subRect.minY / CGFloat(surfaceHeight) - self.subRect.height / CGFloat(surfaceHeight)), size: CGSize(width: self.subRect.width / CGFloat(surfaceWidth), height: self.subRect.height / CGFloat(surfaceHeight))) + } + } + + func allocateIfPossible(renderingParameters: RenderLayerSpec) -> SurfaceAllocation? { + let width = renderingParameters.allocationWidth + let height = renderingParameters.allocationHeight + + let item0 = self.packContext.addItem(withWidth: Int32(width), height: Int32(height)) + let item1 = self.packContext.addItem(withWidth: Int32(width), height: Int32(height)) + + if item0.itemId != -1 && item1.itemId != -1 { + let layout0 = AllocationLayout( + baseRect: CGRect(origin: CGPoint(x: CGFloat(item0.x), y: CGFloat(item0.y)), size: CGSize(width: CGFloat(item0.width), height: CGFloat(item0.height))), + surfaceWidth: self.width, + surfaceHeight: self.height + ) + let layout1 = AllocationLayout( + baseRect: CGRect(origin: CGPoint(x: CGFloat(item1.x), y: CGFloat(item1.y)), size: CGSize(width: CGFloat(item1.width), height: CGFloat(item1.height))), + surfaceWidth: self.width, + surfaceHeight: self.height + ) + + return SurfaceAllocation( + surfaceId: self.id, + allocationId0: item0.itemId, + allocationId1: item1.itemId, + renderingParameters: renderingParameters, + phase0: SurfaceAllocation.Phase( + subRect: layout0.subRect, + renderingRect: layout0.renderingRect, + contentsRect: layout0.contentsRect + ), + phase1: SurfaceAllocation.Phase( + subRect: layout1.subRect, + renderingRect: layout1.renderingRect, + contentsRect: layout1.contentsRect + ) + ) + } else { + if item0.itemId != -1 { + self.packContext.removeItem(item0.itemId) + } + if item1.itemId != -1 { + self.packContext.removeItem(item1.itemId) + } + + return nil + } + } + + func removeAllocation(id: Int32) { + self.packContext.removeItem(id) + } + } + + private final class SubjectReference { + weak var subject: MetalEngineSubject? + + init(subject: MetalEngineSubject) { + self.subject = subject + } + } + + fileprivate final class Impl { + let device: MTLDevice + let library: MTLLibrary + let commandQueue: MTLCommandQueue + let clearPipelineState: MTLRenderPipelineState + + #if targetEnvironment(simulator) + let _layer: CALayer + @available(iOS 13.0, *) + var layer: MetalEventLayer { + return self._layer as! MetalEventLayer + } + #else + let layer: MetalEventLayer + #endif + + var nextSurfaceId: Int = 0 + var surfaces: [Int: Surface] = [:] + + private var nextLayerId: Int = 0 + + private var nextSubjectId: Int = 0 + private var updatedSubjectIds: [Int] = [] + private var updatedSubjects: [SubjectReference] = [] + + private var scheduledClearAllocations: [SurfaceAllocation] = [] + + fileprivate var renderStates: [ObjectIdentifier: RenderToLayerState] = [:] + fileprivate var computeStates: [ObjectIdentifier: ComputeState] = [:] + + init?(device: MTLDevice) { + let mainBundle = Bundle(for: Impl.self) + guard let path = mainBundle.path(forResource: "MetalEngineMetalSourcesBundle", ofType: "bundle") else { + return nil + } + guard let bundle = Bundle(path: path) else { + return nil + } + + self.device = device + + guard let commandQueue = device.makeCommandQueue() else { + return nil + } + self.commandQueue = commandQueue + + guard let library = try? device.makeDefaultLibrary(bundle: bundle) else { + return nil + } + self.library = library + + guard let vertexFunction = library.makeFunction(name: "clearVertex") else { + return nil + } + guard let fragmentFunction = library.makeFunction(name: "clearFragment") else { + return nil + } + + let pipelineDescriptor = MTLRenderPipelineDescriptor() + pipelineDescriptor.vertexFunction = vertexFunction + pipelineDescriptor.fragmentFunction = fragmentFunction + pipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm + guard let clearPipelineState = try? device.makeRenderPipelineState(descriptor: pipelineDescriptor) else { + return nil + } + self.clearPipelineState = clearPipelineState + + #if targetEnvironment(simulator) + if #available(iOS 13.0, *) { + self._layer = MetalEventLayer() + } else { + self._layer = CALayer() + } + #else + self.layer = MetalEventLayer() + #endif + + #if targetEnvironment(simulator) + @available(iOS 13.0, *) + #endif + func configureLayer(layer: MetalEventLayer, device: MTLDevice, impl: Impl) { + layer.drawableSize = CGSize(width: 32, height: 32) + layer.contentsScale = 1.0 + layer.device = device + layer.presentsWithTransaction = true + layer.framebufferOnly = false + layer.onDisplay = { [unowned impl] in + impl.display() + } + } + + #if targetEnvironment(simulator) + if #available(iOS 13.0, *) { + configureLayer(layer: self.layer, device: self.device, impl: self) + } + #else + configureLayer(layer: self.layer, device: self.device, impl: self) + #endif + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func addSurface(width: Int, height: Int) -> Surface? { + let surfaceId = self.nextSurfaceId + self.nextSurfaceId += 1 + + let surface = Surface(id: surfaceId, device: self.device, width: width, height: height) + self.surfaces[surfaceId] = surface + + return surface + } + + private func refreshLayerAllocation(layer: MetalEngineSubjectLayer, renderSpec: RenderLayerSpec) { + var previousSurfaceId: Int? + var updatedSurfaceId: Int? + + if let allocation = layer.surfaceAllocation { + previousSurfaceId = allocation.surfaceId + + if renderSpec != allocation.renderingParameters { + layer.surfaceAllocation = nil + self.scheduledClearAllocations.append(allocation) + } + } + + if layer.internalId != -1 { + let renderingParameters = renderSpec + + if let currentAllocation = layer.surfaceAllocation { + var updatedAllocation = currentAllocation + updatedAllocation.currentPhase = updatedAllocation.currentPhase == 0 ? 1 : 0 + layer.surfaceAllocation = updatedAllocation + updatedSurfaceId = updatedAllocation.surfaceId + layer.contentsRect = updatedAllocation.effectivePhase.contentsRect + } else { + if renderingParameters.allocationWidth >= 1024 || renderingParameters.allocationHeight >= 1024 { + let surfaceWidth = max(1024, alignUp(renderingParameters.allocationWidth * 2, alignment: 64)) + let surfaceHeight = max(512, alignUp(renderingParameters.allocationHeight, alignment: 64)) + + if let surface = self.addSurface(width: surfaceWidth, height: surfaceHeight) { + if let allocation = surface.allocateIfPossible(renderingParameters: renderingParameters) { + layer.surfaceAllocation = allocation + layer.contentsRect = allocation.effectivePhase.contentsRect + updatedSurfaceId = allocation.surfaceId + } + } + } else { + for (_, surface) in self.surfaces { + if let allocation = surface.allocateIfPossible(renderingParameters: renderingParameters) { + layer.surfaceAllocation = allocation + layer.contentsRect = allocation.effectivePhase.contentsRect + updatedSurfaceId = allocation.surfaceId + break + } + } + } + if updatedSurfaceId == nil { + let surfaceWidth = alignUp(2048, alignment: 64) + let surfaceHeight = alignUp(2048, alignment: 64) + + if let surface = self.addSurface(width: surfaceWidth, height: surfaceHeight) { + if let allocation = surface.allocateIfPossible(renderingParameters: renderingParameters) { + layer.surfaceAllocation = allocation + layer.contentsRect = allocation.effectivePhase.contentsRect + updatedSurfaceId = allocation.surfaceId + } + } + } + } + } else { + if let currentAllocation = layer.surfaceAllocation { + layer.surfaceAllocation = nil + self.scheduledClearAllocations.append(currentAllocation) + } + } + + if previousSurfaceId != updatedSurfaceId { + if let updatedSurfaceId { + layer.contents = self.surfaces[updatedSurfaceId]?.ioSurface + + if previousSurfaceId != nil { + #if DEBUG + print("Changing surface for layer \(layer) (\(renderSpec.allocationWidth)x\(renderSpec.allocationHeight))") + #endif + } + } else { + layer.contents = nil + + if layer.internalId != -1 { + #if DEBUG + print("Unable to allocate rendering surface for layer \(layer) (\(renderSpec.allocationWidth)x\(renderSpec.allocationHeight)") + #endif + } + } + } + } + + func removeLayerSurfaceAllocation(layer: MetalEngineSubjectLayer) { + if let allocation = layer.surfaceAllocation { + self.scheduledClearAllocations.append(allocation) + } + } + + func addSubjectNeedsUpdate(subject: MetalEngineSubject) { + let internalData: MetalEngineSubjectInternalData + if let current = subject.internalData { + internalData = current + } else { + internalData = MetalEngineSubjectInternalData() + subject.internalData = internalData + } + + let internalId: Int + if internalData.internalId != -1 { + internalId = internalData.internalId + } else { + internalId = self.nextSubjectId + self.nextSubjectId += 1 + internalData.internalId = internalId + } + + let isFirst = self.updatedSubjectIds.isEmpty + + if !self.updatedSubjectIds.contains(internalId) { + self.updatedSubjectIds.append(internalId) + self.updatedSubjects.append(SubjectReference(subject: subject)) + } + + if isFirst { + #if targetEnvironment(simulator) + if #available(iOS 13.0, *) { + self.layer.setNeedsDisplay() + } + #else + self.layer.setNeedsDisplay() + #endif + } + } + + func display() { + if !self.scheduledClearAllocations.isEmpty { + for allocation in self.scheduledClearAllocations { + if let surface = self.surfaces[allocation.surfaceId] { + surface.removeAllocation(id: allocation.allocationId0) + surface.removeAllocation(id: allocation.allocationId1) + } + } + self.scheduledClearAllocations.removeAll() + + //TODO:remove clear empty surfaces + } + if self.updatedSubjects.isEmpty { + return + } + + let wereActionsDisabled = CATransaction.disableActions() + CATransaction.setDisableActions(true) + defer { + CATransaction.setDisableActions(wereActionsDisabled) + } + + guard let commandBuffer = self.commandQueue.makeCommandBuffer() else { + return + } + + let subjectContext = MetalEngineSubjectContext(device: device, impl: self) + + for subjectReference in self.updatedSubjects { + guard let subject = subjectReference.subject else { + continue + } + subject.update(context: subjectContext) + } + self.updatedSubjects.removeAll() + self.updatedSubjectIds.removeAll() + + if !subjectContext.computeOperations.isEmpty { + for computeOperation in subjectContext.computeOperations { + computeOperation.commands(commandBuffer) + } + } + + if !subjectContext.renderToLayerOperationsGroupedByState.isEmpty { + for (_, renderToLayerOperations) in subjectContext.renderToLayerOperationsGroupedByState { + for renderToLayerOperation in renderToLayerOperations { + guard let layer = renderToLayerOperation.layer else { + continue + } + if layer.internalId == -1 { + layer.internalId = self.nextLayerId + self.nextLayerId += 1 + } + self.refreshLayerAllocation(layer: layer, renderSpec: renderToLayerOperation.spec) + } + } + + var surfaceIds: [Int] = [] + + for (id, surface) in self.surfaces { + surfaceIds.append(id) + + var clearQuads: [SIMD2] = [] + + for (_, renderToLayerOperations) in subjectContext.renderToLayerOperationsGroupedByState { + for renderToLayerOperation in renderToLayerOperations { + guard let layer = renderToLayerOperation.layer else { + continue + } + guard let surfaceAllocation = layer.surfaceAllocation, surfaceAllocation.surfaceId == id else { + continue + } + + let layerRect = surfaceAllocation.effectivePhase.renderingRect + + //let edgeSize = surfaceAllocation.renderingParameters.edgeInset + //let renderSize = CGSize(width: surfaceAllocation.renderingParameters.size.width, height: surfaceAllocation.renderingParameters.size.height) + + //let kx = (CGFloat(edgeSize) * 2.0 + renderSize.width) / layerRect.width + //let ky = (CGFloat(edgeSize) * 2.0 + renderSize.height) / layerRect.height + //let insetX = CGFloat(edgeSize) / kx + //let insetY = CGFloat(edgeSize) / ky + + let quadVertices: [SIMD2] = [ + SIMD2(Float(layerRect.minX), Float(layerRect.minY)), + SIMD2(Float(layerRect.maxX), Float(layerRect.minY)), + SIMD2(Float(layerRect.minX), Float(layerRect.maxY)), + SIMD2(Float(layerRect.maxX), Float(layerRect.minY)), + SIMD2(Float(layerRect.minX), Float(layerRect.maxY)), + SIMD2(Float(layerRect.maxX), Float(layerRect.maxY)) + ].map { v in + var v = v + v.y = -1.0 + v.y * 2.0 + v.x = -1.0 + v.x * 2.0 + return v + } + clearQuads.append(contentsOf: quadVertices) + } + } + + if !subjectContext.renderToLayerOperationsGroupedByState.isEmpty || !clearQuads.isEmpty { + let renderPass = MTLRenderPassDescriptor() + renderPass.colorAttachments[0].texture = surface.texture + renderPass.colorAttachments[0].loadAction = .load + renderPass.colorAttachments[0].storeAction = .store + + guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPass) else { + return + } + + if !clearQuads.isEmpty { + renderEncoder.setRenderPipelineState(self.clearPipelineState) + + //TODO:use buffer if too many vertices + renderEncoder.setVertexBytes(clearQuads, length: 4 * clearQuads.count * 2, index: 0) + var renderingBackgroundColor = SIMD4(0.0, 0.0, 0.0, 0.0) + renderEncoder.setFragmentBytes(&renderingBackgroundColor, length: 4 * 4, index: 0) + renderEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: clearQuads.count) + } + + for (stateId, renderToLayerOperations) in subjectContext.renderToLayerOperationsGroupedByState { + guard let state = self.renderStates[stateId] else { + continue + } + if !renderToLayerOperations.isEmpty { + renderEncoder.setRenderPipelineState(state.pipelineState) + } + for renderToLayerOperation in renderToLayerOperations { + guard let layer = renderToLayerOperation.layer else { + continue + } + guard let surfaceAllocation = layer.surfaceAllocation, surfaceAllocation.surfaceId == id else { + continue + } + + let subRect = surfaceAllocation.effectivePhase.subRect + renderEncoder.setScissorRect(MTLScissorRect(x: Int(subRect.minX), y: surface.height - Int(subRect.maxY), width: Int(subRect.width), height: Int(subRect.height))) + renderToLayerOperation.commands(renderEncoder, RenderLayerPlacement(effectiveRect: surfaceAllocation.effectivePhase.renderingRect)) + } + } + + renderEncoder.endEncoding() + } + } + } + + if !subjectContext.freeResourcesOnCompletion.isEmpty { + let freeResourcesOnCompletion = subjectContext.freeResourcesOnCompletion + commandBuffer.addCompletedHandler { _ in + DispatchQueue.main.async { + for resource in freeResourcesOnCompletion { + resource.free() + } + } + } + } + + var removeSurfaceIds: [Int] = [] + for (id, surface) in self.surfaces { + if surface.isEmpty { + removeSurfaceIds.append(id) + } + } + for id in removeSurfaceIds { + self.surfaces.removeValue(forKey: id) + } + + #if DEBUG + #if targetEnvironment(simulator) + if #available(iOS 13.0, *) { + if let drawable = self.layer.nextDrawable() { + commandBuffer.present(drawable) + } + } + #else + if let drawable = self.layer.nextDrawable() { + commandBuffer.present(drawable) + } + #endif + #endif + + commandBuffer.commit() + commandBuffer.waitUntilScheduled() + } + } + + public static let shared = MetalEngine() + + fileprivate let impl: Impl + + public var rootLayer: CALayer { + #if targetEnvironment(simulator) + return self.impl._layer + #else + return self.impl.layer + #endif + } + + public var device: MTLDevice { + return self.impl.device + } + + private init() { + self.impl = Impl(device: MTLCreateSystemDefaultDevice()!)! + } + + public func pooledTexture(spec: TextureSpec) -> PooledTexture { + return PooledTexture(device: self.device, spec: spec) + } + + public func pooledBuffer(spec: BufferSpec) -> PooledBuffer { + return PooledBuffer(device: self.device, spec: spec) + } + + public func sharedBuffer(spec: BufferSpec) -> SharedBuffer? { + return SharedBuffer(device: self.device, spec: spec) + } +} diff --git a/submodules/MtProtoKit/Sources/MTRequestMessageService.m b/submodules/MtProtoKit/Sources/MTRequestMessageService.m index c78fce6d19b..6c6d9f4b88e 100644 --- a/submodules/MtProtoKit/Sources/MTRequestMessageService.m +++ b/submodules/MtProtoKit/Sources/MTRequestMessageService.m @@ -699,13 +699,9 @@ - (void)mtProto:(MTProto *)__unused mtProto receivedMessage:(MTIncomingMessage * { bool requestFound = false; - int index = -1; - for (MTRequest *request in _requests) - { - index++; - - if (request.requestContext != nil && request.requestContext.messageId == rpcResultMessage.requestMessageId) - { + NSMutableArray *removeRequests = [[NSMutableArray alloc] init]; + for (MTRequest *request in _requests) { + if (request.requestContext != nil && request.requestContext.messageId == rpcResultMessage.requestMessageId) { requestFound = true; bool restartRequest = false; @@ -734,25 +730,20 @@ - (void)mtProto:(MTProto *)__unused mtProto receivedMessage:(MTIncomingMessage * } } - if (rpcResult != nil) - { + if (rpcResult != nil) { if (MTLogEnabled()) { MTLog(@"[MTRequestMessageService#%p response for %" PRId64 " is %@]", self, request.requestContext.messageId, rpcResult); } - } - else - { + } else { if (MTLogEnabled()) { MTLog(@"[MTRequestMessageService#%p response for %" PRId64 " is error: %d: %@]", self, request.requestContext.messageId, (int)rpcError.errorCode, rpcError.errorDescription); } } - if (rpcResult != nil && request.requestContext.willInitializeApi) - { + if (rpcResult != nil && request.requestContext.willInitializeApi) { MTDatacenterAuthInfo *authInfo = [_context authInfoForDatacenterWithId:mtProto.datacenterId selector:authInfoSelector]; - if (![_apiEnvironment.apiInitializationHash isEqualToString:authInfo.authKeyAttributes[@"apiInitializationHash"]]) - { + if (![_apiEnvironment.apiInitializationHash isEqualToString:authInfo.authKeyAttributes[@"apiInitializationHash"]]) { NSMutableDictionary *authKeyAttributes = [[NSMutableDictionary alloc] initWithDictionary:authInfo.authKeyAttributes]; authKeyAttributes[@"apiInitializationHash"] = _apiEnvironment.apiInitializationHash; @@ -761,19 +752,14 @@ - (void)mtProto:(MTProto *)__unused mtProto receivedMessage:(MTIncomingMessage * } } - if (rpcError != nil) - { - if (rpcError.errorCode == 401) - { - if ([rpcError.errorDescription rangeOfString:@"SESSION_PASSWORD_NEEDED"].location != NSNotFound) - { + if (rpcError != nil) { + if (rpcError.errorCode == 401) { + if ([rpcError.errorDescription rangeOfString:@"SESSION_PASSWORD_NEEDED"].location != NSNotFound) { if (!request.passthroughPasswordEntryError) { [_context updatePasswordInputRequiredForDatacenterWithId:mtProto.datacenterId required:true]; } - } - else - { + } else { id delegate = _delegate; if ([delegate respondsToSelector:@selector(requestMessageServiceAuthorizationRequired:)]) { @@ -790,15 +776,12 @@ - (void)mtProto:(MTProto *)__unused mtProto receivedMessage:(MTIncomingMessage * restartRequest = true; } } - } - else if (rpcError.errorCode == -500 || rpcError.errorCode == 500) - { + } else if (rpcError.errorCode == -500 || rpcError.errorCode == 500) { if (request.errorContext == nil) request.errorContext = [[MTRequestErrorContext alloc] init]; request.errorContext.internalServerErrorCount++; - if (request.shouldContinueExecutionWithErrorContext != nil && request.shouldContinueExecutionWithErrorContext(request.errorContext)) - { + if (request.shouldContinueExecutionWithErrorContext != nil && request.shouldContinueExecutionWithErrorContext(request.errorContext)) { restartRequest = true; request.errorContext.minimalExecuteTime = MAX(request.errorContext.minimalExecuteTime, MTAbsoluteSystemTime() + 2.0); } @@ -825,13 +808,11 @@ - (void)mtProto:(MTProto *)__unused mtProto receivedMessage:(MTIncomingMessage * } restartRequest = true; } - else if (rpcError.errorCode == 420 || [rpcError.errorDescription rangeOfString:@"FLOOD_WAIT_"].location != NSNotFound) - { + else if (rpcError.errorCode == 420 || [rpcError.errorDescription rangeOfString:@"FLOOD_WAIT_"].location != NSNotFound) { if (request.errorContext == nil) request.errorContext = [[MTRequestErrorContext alloc] init]; - if ([rpcError.errorDescription rangeOfString:@"FLOOD_WAIT_"].location != NSNotFound) - { + if ([rpcError.errorDescription rangeOfString:@"FLOOD_WAIT_"].location != NSNotFound) { int errorWaitTime = 0; NSScanner *scanner = [[NSScanner alloc] initWithString:rpcError.errorDescription]; @@ -857,10 +838,8 @@ - (void)mtProto:(MTProto *)__unused mtProto receivedMessage:(MTIncomingMessage * } } } - else if (rpcError.errorCode == 400 && [rpcError.errorDescription rangeOfString:@"CONNECTION_NOT_INITED"].location != NSNotFound) - { - [_context performBatchUpdates:^ - { + else if (rpcError.errorCode == 400 && [rpcError.errorDescription rangeOfString:@"CONNECTION_NOT_INITED"].location != NSNotFound) { + [_context performBatchUpdates:^{ MTDatacenterAuthInfo *authInfo = [_context authInfoForDatacenterWithId:mtProto.datacenterId selector:authInfoSelector]; NSMutableDictionary *authKeyAttributes = [[NSMutableDictionary alloc] initWithDictionary:authInfo.authKeyAttributes]; @@ -882,14 +861,10 @@ - (void)mtProto:(MTProto *)__unused mtProto receivedMessage:(MTIncomingMessage * request.requestContext = nil; - if (restartRequest) - { - - } - else - { + if (restartRequest) { + } else { void (^completed)(id result, MTRequestResponseInfo *info, id error) = [request.completed copy]; - [_requests removeObjectAtIndex:(NSUInteger)index]; + [removeRequests addObject:request]; if (completed) { double duration = 0.0; @@ -905,6 +880,10 @@ - (void)mtProto:(MTProto *)__unused mtProto receivedMessage:(MTIncomingMessage * } } + for (MTRequest *request in removeRequests) { + [_requests removeObject:request]; + } + if (!requestFound) { if (MTLogEnabled()) { MTLog(@"[MTRequestMessageService#%p response %" PRId64 " for % " PRId64 " didn't match any request]", self, message.messageId, rpcResultMessage.requestMessageId); diff --git a/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift b/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift index d239e6d175c..75328ee483c 100644 --- a/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift +++ b/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift @@ -573,6 +573,250 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode { private let fadeWidth: CGFloat = 70.0 +private final class VariableBlurView: UIVisualEffectView { + let maxBlurRadius: CGFloat + + var gradientMask: UIImage { + didSet { + if self.gradientMask !== oldValue { + self.resetEffect() + } + } + } + + init(gradientMask: UIImage, maxBlurRadius: CGFloat = 20) { + self.gradientMask = gradientMask + self.maxBlurRadius = maxBlurRadius + + super.init(effect: UIBlurEffect(style: .regular)) + + self.resetEffect() + + if self.subviews.indices.contains(1) { + let tintOverlayView = subviews[1] + tintOverlayView.alpha = 0 + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func resetEffect() { + let filterClassStringEncoded = "Q0FGaWx0ZXI=" + let filterClassString: String = { + if + let data = Data(base64Encoded: filterClassStringEncoded), + let string = String(data: data, encoding: .utf8) + { + return string + } + + return "" + }() + let filterWithTypeStringEncoded = "ZmlsdGVyV2l0aFR5cGU6" + let filterWithTypeString: String = { + if + let data = Data(base64Encoded: filterWithTypeStringEncoded), + let string = String(data: data, encoding: .utf8) + { + return string + } + + return "" + }() + + let filterWithTypeSelector = Selector(filterWithTypeString) + + guard let filterClass = NSClassFromString(filterClassString) as AnyObject as? NSObjectProtocol else { + return + } + + guard filterClass.responds(to: filterWithTypeSelector) else { + return + } + + let variableBlur = filterClass.perform(filterWithTypeSelector, with: "variableBlur").takeUnretainedValue() + + guard let variableBlur = variableBlur as? NSObject else { + return + } + + guard let gradientImageRef = self.gradientMask.cgImage else { + return + } + + variableBlur.setValue(self.maxBlurRadius, forKey: "inputRadius") + variableBlur.setValue(gradientImageRef, forKey: "inputMaskImage") + variableBlur.setValue(true, forKey: "inputNormalizeEdges") + + let backdropLayer = self.subviews.first?.layer + backdropLayer?.filters = [variableBlur] + } +} + +public final class PeerAvatarBottomShadowNode: ASDisplayNode { + let backgroundNode: NavigationBackgroundNode + private var backgroundView: VariableBlurView? + private var currentBackgroundBlurImage: UIImage? + + private let backgroundGradientMaskLayer: SimpleGradientLayer + public let imageView: UIImageView + + override init() { + self.backgroundNode = NavigationBackgroundNode(color: .black, enableBlur: true) + + self.backgroundGradientMaskLayer = SimpleGradientLayer() + //self.backgroundNode.layer.mask = self.backgroundGradientMaskLayer + + self.imageView = UIImageView() + self.imageView.contentMode = .scaleToFill + + super.init() + + //self.backgroundColor = UIColor.blue.withAlphaComponent(0.5) + + self.backgroundGradientMaskLayer.type = .axial + self.backgroundGradientMaskLayer.startPoint = CGPoint(x: 0.0, y: 1.0) + self.backgroundGradientMaskLayer.endPoint = CGPoint(x: 0.0, y: 0.0) + + let baseGradientAlpha: CGFloat = 1.0 + let numSteps = 8 + let firstStep = 1 + let firstLocation = 0.8 + self.backgroundGradientMaskLayer.colors = (0 ..< numSteps).map { i in + if i < firstStep { + return UIColor(white: 1.0, alpha: 1.0).cgColor + } else { + let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) + let value: CGFloat = 1.0 - bezierPoint(0.42, 0.0, 0.58, 1.0, step) + return UIColor(white: 1.0, alpha: baseGradientAlpha * value).cgColor + } + } + self.backgroundGradientMaskLayer.locations = (0 ..< numSteps).map { i -> NSNumber in + if i < firstStep { + return 0.0 as NSNumber + } else { + let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) + return (firstLocation + (1.0 - firstLocation) * step) as NSNumber + } + } + + self.backgroundNode.updateColor(color: UIColor(white: 0.0, alpha: 0.1), enableSaturation: false, forceKeepBlur: true, transition: .immediate) + + //self.addSubnode(self.backgroundNode) + } + + public func update(size: CGSize, transition: ContainedViewLayoutTransition) { + transition.updateFrame(view: self.imageView, frame: CGRect(origin: CGPoint(), size: size), beginWithCurrentState: true) + + transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size), beginWithCurrentState: true) + transition.updateFrame(layer: self.backgroundGradientMaskLayer, frame: CGRect(origin: CGPoint(), size: size), beginWithCurrentState: true) + self.backgroundNode.update(size: size, transition: transition) + + let backgroundBlurImage: UIImage + if let currentBackgroundBlurImage = self.currentBackgroundBlurImage, currentBackgroundBlurImage.size.height == size.height { + backgroundBlurImage = currentBackgroundBlurImage + } else { + let baseGradientAlpha: CGFloat = 0.5 + let numSteps = 8 + let firstStep = 1 + let firstLocation = 0.5 + let colors = (0 ..< numSteps).map { i -> UIColor in + if i < firstStep { + return UIColor(white: 1.0, alpha: 1.0) + } else { + let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) + let value: CGFloat = 1.0 - bezierPoint(0.42, 0.0, 0.58, 1.0, step) + return UIColor(white: 1.0, alpha: baseGradientAlpha * value) + } + } + let locations = (0 ..< numSteps).map { i -> CGFloat in + if i < firstStep { + return 0.0 + } else { + let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) + return (firstLocation + (1.0 - firstLocation) * step) + } + } + + backgroundBlurImage = generateGradientImage(size: CGSize(width: 8.0, height: size.height), colors: colors.reversed(), locations: locations.reversed().map { 1.0 - $0 })! + } + if let backgroundView = self.backgroundView { + if self.currentBackgroundBlurImage !== backgroundBlurImage { + backgroundView.gradientMask = backgroundBlurImage + } + backgroundView.frame = CGRect(origin: CGPoint(), size: size) + } else { + self.currentBackgroundBlurImage = backgroundBlurImage + let backgroundView = VariableBlurView(gradientMask: backgroundBlurImage, maxBlurRadius: 15.0) + backgroundView.layer.mask = self.backgroundGradientMaskLayer + self.backgroundView = backgroundView + self.view.addSubview(backgroundView) + backgroundView.frame = CGRect(origin: CGPoint(), size: size) + } + } +} + +public final class AvatarListContentNode: ASDisplayNode { + final class View: UIView { + override static var layerClass: AnyClass { + return CAReplicatorLayer.self + } + + override init(frame: CGRect) { + super.init(frame: frame) + + let replicatorLayer = self.layer as! CAReplicatorLayer + replicatorLayer.instanceCount = 2 + + self.backgroundColor = nil + self.isOpaque = false + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(size: CGSize) { + var instanceTransform = CATransform3DIdentity + instanceTransform = CATransform3DTranslate(instanceTransform, 0.0, (size.width - (size.height - size.width)) * 2.0 - 4.0, 0.0) + instanceTransform = CATransform3DScale(instanceTransform, 1.0, -3.0, 1.0) + + let replicatorLayer = self.layer as! CAReplicatorLayer + replicatorLayer.instanceTransform = instanceTransform + } + + func updateIsInPinchMode(_ value: Bool) { + let replicatorLayer = self.layer as! CAReplicatorLayer + + if value { + replicatorLayer.instanceAlphaOffset = -1.0 + replicatorLayer.animate(from: 0.0 as NSNumber, to: -1.0 as NSNumber, keyPath: "instanceAlphaOffset", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 0.3) + } else { + replicatorLayer.instanceAlphaOffset = 0.0 + replicatorLayer.animate(from: -1.0 as NSNumber, to: 1.0 as NSNumber, keyPath: "instanceAlphaOffset", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 0.3) + } + } + } + + override public init() { + super.init() + + self.setViewBlock({ + return View(frame: CGRect()) + }) + } + + public func update(size: CGSize) { + (self.view as? View)?.update(size: size) + } + + public func updateIsInPinchMode(_ value: Bool) { + (self.view as? View)?.updateIsInPinchMode(value) + } +} + public final class PeerInfoAvatarListContainerNode: ASDisplayNode { private let context: AccountContext private let isSettings: Bool @@ -582,7 +826,7 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { public let controlsClippingNode: ASDisplayNode public let controlsClippingOffsetNode: ASDisplayNode public let topShadowNode: ASImageNode - public let bottomShadowNode: ASImageNode + public let bottomShadowNode: PeerAvatarBottomShadowNode public var storyParams: (peer: EnginePeer, items: [EngineStoryItem], count: Int, hasUnseen: Bool, hasUnseenPrivate: Bool)? private var expandedStorySetIndicator: ComponentView? @@ -594,7 +838,7 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { } } - public let contentNode: ASDisplayNode + public let contentNode: AvatarListContentNode let leftHighlightNode: ASDisplayNode let rightHighlightNode: ASDisplayNode var highlightedSide: Bool? @@ -728,7 +972,7 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { self.context = context self.isSettings = isSettings - self.contentNode = ASDisplayNode() + self.contentNode = AvatarListContentNode() self.leftHighlightNode = ASDisplayNode() self.leftHighlightNode.displaysAsynchronously = false @@ -802,10 +1046,7 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { self.topShadowNode.displayWithoutProcessing = true self.topShadowNode.contentMode = .scaleToFill - self.bottomShadowNode = ASImageNode() - self.bottomShadowNode.displaysAsynchronously = false - self.bottomShadowNode.displayWithoutProcessing = true - self.bottomShadowNode.contentMode = .scaleToFill + self.bottomShadowNode = PeerAvatarBottomShadowNode() do { let size = CGSize(width: 88.0, height: 88.0) @@ -834,7 +1075,7 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0) context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size)) }) - self.bottomShadowNode.image = generateImage(image.size, contextGenerator: { size, context in + self.bottomShadowNode.imageView.image = generateImage(image.size, contextGenerator: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) context.translateBy(x: size.width / 2.0, y: size.height / 2.0) context.rotate(by: CGFloat.pi / 2.0) @@ -847,7 +1088,7 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { super.init() - self.backgroundColor = .black + //self.backgroundColor = .black self.addSubnode(self.contentNode) @@ -1448,7 +1689,8 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { itemNode.delayCentralityLose = false let indexOffset = CGFloat(i - self.currentIndex) - let itemFrame = CGRect(origin: CGPoint(x: indexOffset * size.width + self.transitionFraction * size.width - size.width / 2.0, y: -size.height / 2.0), size: size) + var itemFrame = CGRect(origin: CGPoint(x: indexOffset * size.width + self.transitionFraction * size.width - size.width / 2.0, y: -size.height / 2.0), size: size) + itemFrame.origin.y -= (size.height - size.width) * 0.5 if wasAdded { itemsAdded = true diff --git a/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift b/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift index c2f4b1f6401..f55b840c697 100644 --- a/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift +++ b/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift @@ -663,7 +663,7 @@ private func deviceContactInfoEntries(account: Account, engine: TelegramEngine, firstName = presentationData.strings.Message_Contact } - entries.append(.info(entries.count, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer: peer ?? EnginePeer.user(TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: firstName, lastName: isOrganization ? nil : personName.1, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil)), state: ItemListAvatarAndNameInfoItemState(editingName: editingName, updatingName: nil), job: isOrganization ? nil : jobSummary, isPlain: !isShare, hiddenAvatar: hiddenAvatar)) + entries.append(.info(entries.count, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer: peer ?? EnginePeer.user(TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: firstName, lastName: isOrganization ? nil : personName.1, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)), state: ItemListAvatarAndNameInfoItemState(editingName: editingName, updatingName: nil), job: isOrganization ? nil : jobSummary, isPlain: !isShare, hiddenAvatar: hiddenAvatar)) if !selecting { if let _ = peer { diff --git a/submodules/Postbox/Sources/CachedPeerDataTable.swift b/submodules/Postbox/Sources/CachedPeerDataTable.swift index 1d61516e9c8..fbd5dadf674 100644 --- a/submodules/Postbox/Sources/CachedPeerDataTable.swift +++ b/submodules/Postbox/Sources/CachedPeerDataTable.swift @@ -9,7 +9,7 @@ final class CachedPeerDataTable: Table { private let sharedKey = ValueBoxKey(length: 8) private var cachedDatas: [PeerId: CachedPeerData] = [:] - private var updatedPeerIds = Set() + private var updatedPeers: [PeerId: (CachedPeerData?, CachedPeerData)] = [:] override init(valueBox: ValueBox, table: ValueBoxTable, useCaches: Bool) { super.init(valueBox: valueBox, table: table, useCaches: useCaches) @@ -21,8 +21,15 @@ final class CachedPeerDataTable: Table { } func set(id: PeerId, data: CachedPeerData) { + var previousValue: CachedPeerData? + if let currentUpdate = self.updatedPeers[id] { + previousValue = currentUpdate.0 + } else { + previousValue = self.get(id) + } + self.cachedDatas[id] = data - self.updatedPeerIds.insert(id) + self.updatedPeers[id] = (previousValue, data) } func get(_ id: PeerId) -> CachedPeerData? { @@ -40,11 +47,15 @@ final class CachedPeerDataTable: Table { override func clearMemoryCache() { self.cachedDatas.removeAll() - self.updatedPeerIds.removeAll() + self.updatedPeers.removeAll() + } + + func transactionUpdatedPeers() -> [PeerId: (CachedPeerData?, CachedPeerData)] { + return self.updatedPeers } override func beforeCommit() { - for peerId in self.updatedPeerIds { + for peerId in self.updatedPeers.keys { if let data = self.cachedDatas[peerId] { self.sharedEncoder.reset() self.sharedEncoder.encodeRootObject(data) @@ -53,7 +64,7 @@ final class CachedPeerDataTable: Table { } } - self.updatedPeerIds.removeAll() + self.updatedPeers.removeAll() if !self.useCaches { self.cachedDatas.removeAll() } diff --git a/submodules/Postbox/Sources/ChatListIndexTable.swift b/submodules/Postbox/Sources/ChatListIndexTable.swift index 44519860602..510fce50fac 100644 --- a/submodules/Postbox/Sources/ChatListIndexTable.swift +++ b/submodules/Postbox/Sources/ChatListIndexTable.swift @@ -170,9 +170,21 @@ final class ChatListIndexTable: Table { assert(self.updatedPreviousPeerCachedIndices.isEmpty) } - func commitWithTransaction(postbox: PostboxImpl, currentTransaction: Transaction, alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], updatedPeers: [((Peer, Bool)?, (Peer, Bool))], transactionParticipationInTotalUnreadCountUpdates: (added: Set, removed: Set), alteredInitialPeerThreadsSummaries: [PeerId: StoredPeerThreadsSummary], updatedTotalUnreadStates: inout [PeerGroupId: ChatListTotalUnreadState], updatedGroupTotalUnreadSummaries: inout [PeerGroupId: PeerGroupUnreadCountersCombinedSummary], currentUpdatedGroupSummarySynchronizeOperations: inout [PeerGroupAndNamespace: Bool]) { + func commitWithTransaction( + postbox: PostboxImpl, + currentTransaction: Transaction, + alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], + updatedPeers: [((Peer, Bool)?, (Peer, Bool))], + updatedCachedPeerData: [PeerId: (CachedPeerData?, CachedPeerData)], + transactionParticipationInTotalUnreadCountUpdates: (added: Set, removed: Set), + alteredInitialPeerThreadsSummaries: [PeerId: StoredPeerThreadsSummary], + updatedTotalUnreadStates: inout [PeerGroupId: ChatListTotalUnreadState], + updatedGroupTotalUnreadSummaries: inout [PeerGroupId: PeerGroupUnreadCountersCombinedSummary], + currentUpdatedGroupSummarySynchronizeOperations: inout [PeerGroupAndNamespace: Bool] + ) { var updatedPeerTags: [PeerId: (previous: PeerSummaryCounterTags, updated: PeerSummaryCounterTags)] = [:] - var updatedIsThreadBased: [PeerId: Bool] = [:] + var updatedIsThreadBasedUnreadCountCalculation: [PeerId: Bool] = [:] + for (previous, updated) in updatedPeers { let previousTags: PeerSummaryCounterTags if let (previous, previousIsContact) = previous { @@ -186,14 +198,51 @@ final class ChatListIndexTable: Table { } if let previous = previous { - let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(updated.0) - if postbox.seedConfiguration.peerSummaryIsThreadBased(previous.0) != isThreadBased { - updatedIsThreadBased[updated.0.id] = isThreadBased + var isThreadBasedUnreadCalculation = postbox.seedConfiguration.peerSummaryIsThreadBased(updated.0) + if let cachedData = updatedCachedPeerData[updated.0.id]?.1, postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedData) { + isThreadBasedUnreadCalculation = false + } + + var wasThreadBasedUnreadCalculation = false + if postbox.seedConfiguration.peerSummaryIsThreadBased(previous.0) { + if let cachedData = postbox.cachedPeerDataTable.get(previous.0.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedData) { + } else { + wasThreadBasedUnreadCalculation = true + } + } + + if wasThreadBasedUnreadCalculation != isThreadBasedUnreadCalculation { + updatedIsThreadBasedUnreadCountCalculation[updated.0.id] = isThreadBasedUnreadCalculation + } + } + } + for (peerId, cachedDataUpdate) in updatedCachedPeerData { + if updatedIsThreadBasedUnreadCountCalculation[peerId] != nil { + continue + } + guard let peer = postbox.peerTable.get(peerId) else { + continue + } + + var isThreadBasedUnreadCalculation = postbox.seedConfiguration.peerSummaryIsThreadBased(peer) + if postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedDataUpdate.1) { + isThreadBasedUnreadCalculation = false + } + + var wasThreadBasedUnreadCalculation = false + if postbox.seedConfiguration.peerSummaryIsThreadBased(peer) { + if let previousCachedData = cachedDataUpdate.0, postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(previousCachedData) { + } else { + wasThreadBasedUnreadCalculation = true } } + + if wasThreadBasedUnreadCalculation != isThreadBasedUnreadCalculation { + updatedIsThreadBasedUnreadCountCalculation[peerId] = isThreadBasedUnreadCalculation + } } - if !self.updatedPreviousPeerCachedIndices.isEmpty || !alteredInitialPeerCombinedReadStates.isEmpty || !updatedPeerTags.isEmpty || !transactionParticipationInTotalUnreadCountUpdates.added.isEmpty || !transactionParticipationInTotalUnreadCountUpdates.removed.isEmpty || !alteredInitialPeerThreadsSummaries.isEmpty { + if !self.updatedPreviousPeerCachedIndices.isEmpty || !alteredInitialPeerCombinedReadStates.isEmpty || !updatedPeerTags.isEmpty || !transactionParticipationInTotalUnreadCountUpdates.added.isEmpty || !transactionParticipationInTotalUnreadCountUpdates.removed.isEmpty || !alteredInitialPeerThreadsSummaries.isEmpty || !updatedCachedPeerData.isEmpty { var addedToGroupPeerIds: [PeerId: PeerGroupId] = [:] var removedFromGroupPeerIds: [PeerId: PeerGroupId] = [:] var addedToIndexPeerIds = Set() @@ -288,12 +337,13 @@ final class ChatListIndexTable: Table { alteredPeerIds.formUnion(removedFromGroupPeerIds.keys) alteredPeerIds.formUnion(transactionParticipationInTotalUnreadCountUpdates.added) alteredPeerIds.formUnion(transactionParticipationInTotalUnreadCountUpdates.removed) + alteredPeerIds.formUnion(updatedCachedPeerData.keys) for peerId in updatedPeerTags.keys { alteredPeerIds.insert(peerId) } - for peerId in updatedIsThreadBased.keys { + for peerId in updatedIsThreadBasedUnreadCountCalculation.keys { alteredPeerIds.insert(peerId) } @@ -365,7 +415,7 @@ final class ChatListIndexTable: Table { let notificationPeerId: PeerId = peer.associatedPeerId ?? peerId let initialReadState: CombinedPeerReadState? - if let updated = updatedIsThreadBased[peerId] { + if let updated = updatedIsThreadBasedUnreadCountCalculation[peerId] { if updated { // was not thread-based, use peer read state initialReadState = alteredInitialPeerCombinedReadStates[peerId] ?? postbox.readStateTable.getCombinedState(peerId) @@ -379,7 +429,12 @@ final class ChatListIndexTable: Table { initialReadState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 1, maxKnownId: 0, count: previousCount, markedUnread: false))]) } } else { - if let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer) { + var displayAsRegularChat = false + if let cachedData = postbox.cachedPeerDataTable.get(peerId), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedData) { + displayAsRegularChat = true + } + + if let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer), !displayAsRegularChat { let previousCount: Int32 if let previousSummary = alteredInitialPeerThreadsSummaries[peerId] { previousCount = previousSummary.effectiveUnreadCount @@ -392,8 +447,13 @@ final class ChatListIndexTable: Table { } } + var displayAsRegularChat = false + if let cachedData = postbox.cachedPeerDataTable.get(peerId), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedData) { + displayAsRegularChat = true + } + let currentReadState: CombinedPeerReadState? - if let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer) { + if let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer), !displayAsRegularChat { let count = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0 currentReadState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 1, maxKnownId: 0, count: count, markedUnread: false))]) } else { diff --git a/submodules/Postbox/Sources/ChatListView.swift b/submodules/Postbox/Sources/ChatListView.swift index b3ac6568948..3a37963f16f 100644 --- a/submodules/Postbox/Sources/ChatListView.swift +++ b/submodules/Postbox/Sources/ChatListView.swift @@ -297,6 +297,7 @@ enum MutableChatListEntry: Equatable { var readState: ChatListViewReadState? var notificationSettings: PeerNotificationSettings? var isRemovedFromTotalUnreadCount: Bool + var displayAsRegularChat: Bool var embeddedInterfaceState: StoredPeerChatInterfaceState? var renderedPeer: RenderedPeer var presence: PeerPresence? @@ -314,6 +315,7 @@ enum MutableChatListEntry: Equatable { readState: ChatListViewReadState?, notificationSettings: PeerNotificationSettings?, isRemovedFromTotalUnreadCount: Bool, + displayAsRegularChat: Bool, embeddedInterfaceState: StoredPeerChatInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, @@ -330,6 +332,7 @@ enum MutableChatListEntry: Equatable { self.readState = readState self.notificationSettings = notificationSettings self.isRemovedFromTotalUnreadCount = isRemovedFromTotalUnreadCount + self.displayAsRegularChat = displayAsRegularChat self.embeddedInterfaceState = embeddedInterfaceState self.renderedPeer = renderedPeer self.presence = presence @@ -759,7 +762,13 @@ final class MutableChatListView { if transaction.alteredInitialPeerCombinedReadStates[groupEntries[i].renderedPeers[j].peer.peerId] != nil { let isUnread: Bool - if let peer = groupEntries[i].renderedPeers[j].peer.peer, postbox.seedConfiguration.peerSummaryIsThreadBased(peer) { + + var displayAsRegularChat: Bool = false + if let cachedData = postbox.cachedPeerDataTable.get(groupEntries[i].renderedPeers[j].peer.peerId) { + displayAsRegularChat = postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedData) + } + + if let peer = groupEntries[i].renderedPeers[j].peer.peer, postbox.seedConfiguration.peerSummaryIsThreadBased(peer), !displayAsRegularChat { isUnread = postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.hasUnmutedUnread ?? false } else { isUnread = postbox.readStateTable.getCombinedState(groupEntries[i].renderedPeers[j].peer.peerId)?.isUnread ?? false @@ -864,7 +873,14 @@ final class MutableChatListView { } let readState: ChatListViewReadState? - if let peer = postbox.peerTable.get(index.messageIndex.id.peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer) { + var displayAsRegularChat: Bool = false + var autoremoveTimeout: Int32? + if let cachedData = postbox.cachedPeerDataTable.get(index.messageIndex.id.peerId) { + autoremoveTimeout = postbox.seedConfiguration.decodeAutoremoveTimeout(cachedData) + displayAsRegularChat = postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedData) + } + + if let peer = postbox.peerTable.get(index.messageIndex.id.peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer), !displayAsRegularChat { let summary = postbox.peerThreadsSummaryTable.get(peerId: index.messageIndex.id.peerId) var count: Int32 = 0 var isMuted: Bool = false @@ -881,11 +897,6 @@ final class MutableChatListView { } } - var autoremoveTimeout: Int32? - if let cachedData = postbox.cachedPeerDataTable.get(index.messageIndex.id.peerId) { - autoremoveTimeout = postbox.seedConfiguration.decodeAutoremoveTimeout(cachedData) - } - let storyStats = fetchPeerStoryStats(postbox: postbox, peerId: index.messageIndex.id.peerId) return .MessageEntry(MutableChatListEntry.MessageEntryData( @@ -894,6 +905,7 @@ final class MutableChatListView { readState: readState, notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: false, + displayAsRegularChat: displayAsRegularChat, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId), renderedPeer: renderedPeer, presence: presence, @@ -943,8 +955,8 @@ public final class ChatListView { renderedPeer: entryData.renderedPeer, presence: entryData.presence, summaryInfo: entryData.tagSummaryInfo, - forumTopicData: entryData.forumTopicData, - topForumTopics: entryData.topForumTopics, + forumTopicData: entryData.displayAsRegularChat ? nil : entryData.forumTopicData, + topForumTopics: entryData.displayAsRegularChat ? [] : entryData.topForumTopics, hasFailed: entryData.hasFailedMessages, isContact: entryData.isContact, autoremoveTimeout: entryData.autoremoveTimeout, @@ -977,8 +989,8 @@ public final class ChatListView { renderedPeer: entryData.renderedPeer, presence: entryData.presence, summaryInfo: entryData.tagSummaryInfo, - forumTopicData: entryData.forumTopicData, - topForumTopics: entryData.topForumTopics, + forumTopicData: entryData.displayAsRegularChat ? nil : entryData.forumTopicData, + topForumTopics: entryData.displayAsRegularChat ? [] : entryData.topForumTopics, hasFailed: entryData.hasFailedMessages, isContact: entryData.isContact, autoremoveTimeout: entryData.autoremoveTimeout, diff --git a/submodules/Postbox/Sources/ChatListViewState.swift b/submodules/Postbox/Sources/ChatListViewState.swift index 9299a2d8e5f..35d3d44dbe8 100644 --- a/submodules/Postbox/Sources/ChatListViewState.swift +++ b/submodules/Postbox/Sources/ChatListViewState.swift @@ -60,7 +60,12 @@ private func mappedChatListFilterPredicate(postbox: PostboxImpl, currentTransact case let .message(index, _): if let peer = postbox.peerTable.get(index.messageIndex.id.peerId) { var isUnread: Bool - if postbox.seedConfiguration.peerSummaryIsThreadBased(peer) { + + var displayAsRegularChat = false + if let cachedPeerData = postbox.cachedPeerDataTable.get(peer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) { + displayAsRegularChat = true + } + if postbox.seedConfiguration.peerSummaryIsThreadBased(peer) && !displayAsRegularChat { isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.effectiveUnreadCount ?? 0) > 0 } else { isUnread = postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId)?.isUnread ?? false @@ -424,12 +429,17 @@ private final class ChatListViewSpaceState { globalNotificationSettings = globalNotificationSettingsValue } + var displayAsRegularChat = false + if let cachedPeerData = postbox.cachedPeerDataTable.get(peer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) { + displayAsRegularChat = true + } + let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettingsValue, peer: peer, peerSettings: postbox.peerNotificationSettingsTable.getEffective(notificationsPeerId)) let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: peer.id, threadId: nil, calculation: filterPredicate.messageTagSummary) var isUnread: Bool - if postbox.seedConfiguration.peerSummaryIsThreadBased(peer) { + if postbox.seedConfiguration.peerSummaryIsThreadBased(peer) && !displayAsRegularChat { isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.effectiveUnreadCount ?? 0) > 0 } else { isUnread = postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId)?.isUnread ?? false @@ -527,7 +537,7 @@ private final class ChatListViewSpaceState { } } - if (!transaction.currentUpdatedPeerNotificationSettings.isEmpty || !transaction.updatedPeerThreadsSummaries.isEmpty), case let .group(groupId, pinned, maybeFilterPredicate) = self.space, let filterPredicate = maybeFilterPredicate { + if (!transaction.currentUpdatedPeerNotificationSettings.isEmpty || !transaction.updatedPeerThreadsSummaries.isEmpty || !transaction.currentUpdatedCachedPeerData.isEmpty), case let .group(groupId, pinned, maybeFilterPredicate) = self.space, let filterPredicate = maybeFilterPredicate { var removeEntryIndices: [MutableChatListEntryIndex] = [] let _ = self.orderedEntries.mutableScan { entry -> MutableChatListEntry? in let entryPeer: Peer @@ -552,9 +562,14 @@ private final class ChatListViewSpaceState { } let settingsChange = transaction.currentUpdatedPeerNotificationSettings[entryNotificationsPeerId] - if settingsChange != nil || transaction.updatedPeerThreadsSummaries.contains(entryNotificationsPeerId) { + if settingsChange != nil || transaction.updatedPeerThreadsSummaries.contains(entryNotificationsPeerId) || transaction.currentUpdatedCachedPeerData[entryNotificationsPeerId] != nil { + var displayAsRegularChat = false + if let cachedPeerData = postbox.cachedPeerDataTable.get(entryPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) { + displayAsRegularChat = true + } + var isUnread: Bool - if postbox.seedConfiguration.peerSummaryIsThreadBased(entryPeer) { + if postbox.seedConfiguration.peerSummaryIsThreadBased(entryPeer) && !displayAsRegularChat { isUnread = (postbox.peerThreadsSummaryTable.get(peerId: entryPeer.id)?.effectiveUnreadCount ?? 0) > 0 } else { isUnread = postbox.readStateTable.getCombinedState(entryPeer.id)?.isUnread ?? false @@ -587,7 +602,7 @@ private final class ChatListViewSpaceState { } } - for peerId in transaction.updatedPeerThreadsSummaries.union(transaction.currentUpdatedPeerNotificationSettings.keys) { + for peerId in transaction.updatedPeerThreadsSummaries.union(transaction.currentUpdatedPeerNotificationSettings.keys).union(transaction.currentUpdatedCachedPeerData.keys) { if let mainPeer = postbox.peerTable.get(peerId) { var peers: [Peer] = [mainPeer] for associatedId in postbox.reverseAssociatedPeerTable.get(peerId: mainPeer.id) { @@ -597,8 +612,13 @@ private final class ChatListViewSpaceState { } assert(Set(peers.map { $0.id }).count == peers.count) + var displayAsRegularChat = false + if let cachedPeerData = postbox.cachedPeerDataTable.get(mainPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) { + displayAsRegularChat = true + } + var isUnread: Bool - if postbox.seedConfiguration.peerSummaryIsThreadBased(mainPeer) { + if postbox.seedConfiguration.peerSummaryIsThreadBased(mainPeer) && !displayAsRegularChat { isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0) > 0 } else { isUnread = postbox.readStateTable.getCombinedState(peerId)?.isUnread ?? false @@ -746,7 +766,7 @@ private final class ChatListViewSpaceState { } } - if !transaction.currentUpdatedMessageTagSummaries.isEmpty || !transaction.currentUpdatedMessageActionsSummaries.isEmpty, case let .group(groupId, pinned, maybeFilterPredicate) = self.space, let filterPredicate = maybeFilterPredicate, let filterMessageTagSummary = filterPredicate.messageTagSummary { + if !transaction.currentUpdatedMessageTagSummaries.isEmpty || !transaction.currentUpdatedMessageActionsSummaries.isEmpty, !transaction.currentUpdatedCachedPeerData.isEmpty, case let .group(groupId, pinned, maybeFilterPredicate) = self.space, let filterPredicate = maybeFilterPredicate, let filterMessageTagSummary = filterPredicate.messageTagSummary { var removeEntryIndices: [MutableChatListEntryIndex] = [] let _ = self.orderedEntries.mutableScan { entry -> MutableChatListEntry? in let entryPeer: Peer @@ -774,8 +794,13 @@ private final class ChatListViewSpaceState { let updatedActionsSummary = transaction.currentUpdatedMessageActionsSummaries[PendingMessageActionsSummaryKey(type: filterMessageTagSummary.subtractCount.type, peerId: entryPeer.id, namespace: filterMessageTagSummary.subtractCount.namespace)] if updatedMessageSummary != nil || updatedActionsSummary != nil { + var displayAsRegularChat = false + if let cachedPeerData = postbox.cachedPeerDataTable.get(entryPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) { + displayAsRegularChat = true + } + var isUnread: Bool - if postbox.seedConfiguration.peerSummaryIsThreadBased(entryPeer) { + if postbox.seedConfiguration.peerSummaryIsThreadBased(entryPeer) && !displayAsRegularChat { isUnread = (postbox.peerThreadsSummaryTable.get(peerId: entryPeer.id)?.effectiveUnreadCount ?? 0) > 0 } else { isUnread = postbox.readStateTable.getCombinedState(entryPeer.id)?.isUnread ?? false @@ -814,6 +839,9 @@ private final class ChatListViewSpaceState { for key in transaction.currentUpdatedMessageTagSummaries.keys { changedPeerIds.insert(key.peerId) } + for peerId in transaction.currentUpdatedCachedPeerData.keys { + changedPeerIds.insert(peerId) + } for peerId in changedPeerIds { if let mainPeer = postbox.peerTable.get(peerId) { var peers: [Peer] = [mainPeer] @@ -824,8 +852,13 @@ private final class ChatListViewSpaceState { } assert(Set(peers.map { $0.id }).count == peers.count) + var displayAsRegularChat = false + if let cachedPeerData = postbox.cachedPeerDataTable.get(mainPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) { + displayAsRegularChat = true + } + var isUnread: Bool - if postbox.seedConfiguration.peerSummaryIsThreadBased(mainPeer) { + if postbox.seedConfiguration.peerSummaryIsThreadBased(mainPeer) && !displayAsRegularChat { isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0) > 0 } else { isUnread = postbox.readStateTable.getCombinedState(peerId)?.isUnread ?? false @@ -913,8 +946,13 @@ private final class ChatListViewSpaceState { } } + var displayAsRegularChat = false + if let cachedPeerData = postbox.cachedPeerDataTable.get(entryData.index.messageIndex.id.peerId), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) { + displayAsRegularChat = true + } + var updatedReadState = entryData.readState - if let peer = postbox.peerTable.get(entryData.index.messageIndex.id.peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer) { + if let peer = postbox.peerTable.get(entryData.index.messageIndex.id.peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer), !displayAsRegularChat { let summary = postbox.peerThreadsSummaryTable.get(peerId: peer.id) var count: Int32 = 0 @@ -956,6 +994,7 @@ private final class ChatListViewSpaceState { entryData.tagSummaryInfo = updatedChatListMessageTagSummaryInfo entryData.autoremoveTimeout = updatedAutoremoveTimeout entryData.storyStats = storyStats + entryData.displayAsRegularChat = displayAsRegularChat return .MessageEntry(entryData) } else { return nil @@ -1535,10 +1574,17 @@ struct ChatListViewState { } } + var autoremoveTimeout: Int32? + var displayAsRegularChat: Bool = false + if let cachedData = postbox.cachedPeerDataTable.get(index.messageIndex.id.peerId) { + autoremoveTimeout = postbox.seedConfiguration.decodeAutoremoveTimeout(cachedData) + displayAsRegularChat = postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedData) + } + var topForumTopics: [ChatListForumTopicData] = [] let readState: ChatListViewReadState? - if let peer = postbox.peerTable.get(index.messageIndex.id.peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer) { + if let peer = postbox.peerTable.get(index.messageIndex.id.peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer), !displayAsRegularChat { for item in postbox.messageHistoryThreadIndexTable.fetch(peerId: peer.id, namespace: 0, start: .upperBound, end: .lowerBound, limit: 5) { topForumTopics.append(ChatListForumTopicData(id: item.threadId, info: item.info)) } @@ -1592,11 +1638,6 @@ struct ChatListViewState { } } - var autoremoveTimeout: Int32? - if let cachedData = postbox.cachedPeerDataTable.get(index.messageIndex.id.peerId) { - autoremoveTimeout = postbox.seedConfiguration.decodeAutoremoveTimeout(cachedData) - } - let storyStats = fetchPeerStoryStats(postbox: postbox, peerId: index.messageIndex.id.peerId) let updatedEntry: MutableChatListEntry = .MessageEntry(MutableChatListEntry.MessageEntryData( @@ -1605,6 +1646,7 @@ struct ChatListViewState { readState: readState, notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, + displayAsRegularChat: displayAsRegularChat, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId), renderedPeer: renderedPeer, presence: presence, diff --git a/submodules/Postbox/Sources/Message.swift b/submodules/Postbox/Sources/Message.swift index 24bec91520c..a1300a3ec5e 100644 --- a/submodules/Postbox/Sources/Message.swift +++ b/submodules/Postbox/Sources/Message.swift @@ -646,11 +646,13 @@ public final class Message { public let title: String public let icon: Int64? public let iconColor: Int32 + public let isClosed: Bool - public init(title: String, icon: Int64?, iconColor: Int32) { + public init(title: String, icon: Int64?, iconColor: Int32, isClosed: Bool) { self.title = title self.icon = icon self.iconColor = iconColor + self.isClosed = isClosed } public static func ==(lhs: AssociatedThreadInfo, rhs: AssociatedThreadInfo) -> Bool { @@ -666,6 +668,9 @@ public final class Message { if lhs.iconColor != rhs.iconColor { return false } + if lhs.isClosed != rhs.isClosed { + return false + } return true } } diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 5787fc1ea06..edd4275bcd9 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -2358,12 +2358,24 @@ final class PostboxImpl { } } } + let updatedCachedPeerData = self.cachedPeerDataTable.transactionUpdatedPeers() let transactionParticipationInTotalUnreadCountUpdates = self.peerNotificationSettingsTable.transactionParticipationInTotalUnreadCountUpdates(postbox: self, transaction: currentTransaction) let updatedMessageThreadPeerIds = self.messageHistoryThreadIndexTable.replay(threadsTable: self.messageHistoryThreadsTable, namespaces: self.seedConfiguration.chatMessagesNamespaces, updatedIds: self.messageHistoryThreadsTable.updatedIds) let alteredInitialPeerThreadsSummaries = self.peerThreadsSummaryTable.update(peerIds: updatedMessageThreadPeerIds.union(self.currentUpdatedPeerThreadCombinedStates), indexTable: self.messageHistoryThreadIndexTable, combinedStateTable: self.peerThreadCombinedStateTable, tagsSummaryTable: self.messageHistoryTagsSummaryTable) - self.chatListIndexTable.commitWithTransaction(postbox: self, currentTransaction: currentTransaction, alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, updatedPeers: updatedPeers, transactionParticipationInTotalUnreadCountUpdates: transactionParticipationInTotalUnreadCountUpdates, alteredInitialPeerThreadsSummaries: alteredInitialPeerThreadsSummaries, updatedTotalUnreadStates: &self.currentUpdatedTotalUnreadStates, updatedGroupTotalUnreadSummaries: &self.currentUpdatedGroupTotalUnreadSummaries, currentUpdatedGroupSummarySynchronizeOperations: &self.currentUpdatedGroupSummarySynchronizeOperations) + self.chatListIndexTable.commitWithTransaction( + postbox: self, + currentTransaction: currentTransaction, + alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, + updatedPeers: updatedPeers, + updatedCachedPeerData: updatedCachedPeerData, + transactionParticipationInTotalUnreadCountUpdates: transactionParticipationInTotalUnreadCountUpdates, + alteredInitialPeerThreadsSummaries: alteredInitialPeerThreadsSummaries, + updatedTotalUnreadStates: &self.currentUpdatedTotalUnreadStates, + updatedGroupTotalUnreadSummaries: &self.currentUpdatedGroupTotalUnreadSummaries, + currentUpdatedGroupSummarySynchronizeOperations: &self.currentUpdatedGroupSummarySynchronizeOperations + ) self.peerTable.commitDependentTables() diff --git a/submodules/Postbox/Sources/SeedConfiguration.swift b/submodules/Postbox/Sources/SeedConfiguration.swift index 704eb789561..9b9912281d0 100644 --- a/submodules/Postbox/Sources/SeedConfiguration.swift +++ b/submodules/Postbox/Sources/SeedConfiguration.swift @@ -76,6 +76,7 @@ public final class SeedConfiguration { public let mergeMessageAttributes: ([MessageAttribute], inout [MessageAttribute]) -> Void public let decodeMessageThreadInfo: (CodableEntry) -> Message.AssociatedThreadInfo? public let decodeAutoremoveTimeout: (CachedPeerData) -> Int32? + public let decodeDisplayPeerAsRegularChat: (CachedPeerData) -> Bool public let isPeerUpgradeMessage: (Message) -> Bool public init( @@ -103,6 +104,7 @@ public final class SeedConfiguration { mergeMessageAttributes: @escaping ([MessageAttribute], inout [MessageAttribute]) -> Void, decodeMessageThreadInfo: @escaping (CodableEntry) -> Message.AssociatedThreadInfo?, decodeAutoremoveTimeout: @escaping (CachedPeerData) -> Int32?, + decodeDisplayPeerAsRegularChat: @escaping (CachedPeerData) -> Bool, isPeerUpgradeMessage: @escaping (Message) -> Bool ) { self.globalMessageIdsPeerIdNamespaces = globalMessageIdsPeerIdNamespaces @@ -125,6 +127,7 @@ public final class SeedConfiguration { self.mergeMessageAttributes = mergeMessageAttributes self.decodeMessageThreadInfo = decodeMessageThreadInfo self.decodeAutoremoveTimeout = decodeAutoremoveTimeout + self.decodeDisplayPeerAsRegularChat = decodeDisplayPeerAsRegularChat self.isPeerUpgradeMessage = isPeerUpgradeMessage } } diff --git a/submodules/PremiumUI/Sources/CreateGiveawayController.swift b/submodules/PremiumUI/Sources/CreateGiveawayController.swift index 4897e22b52a..a54f4aeac03 100644 --- a/submodules/PremiumUI/Sources/CreateGiveawayController.swift +++ b/submodules/PremiumUI/Sources/CreateGiveawayController.swift @@ -989,14 +989,8 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio } let tooltipController = UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: title, text: text, customUndoText: nil, timeout: nil, linkAction: { [weak navigationController] _ in - let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.StatsDatacenterId(id: peerId)) - |> deliverOnMainQueue).startStandalone(next: { [weak navigationController] statsDatacenterId in - guard let statsDatacenterId else { - return - } - let statsController = context.sharedContext.makeChannelStatsController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, boosts: true, boostStatus: nil, statsDatacenterId: statsDatacenterId) - navigationController?.pushViewController(statsController) - }) + let statsController = context.sharedContext.makeChannelStatsController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, boosts: true, boostStatus: nil) + navigationController?.pushViewController(statsController) }), elevatedLayout: false, action: { _ in return true }) @@ -1068,14 +1062,8 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio let text = presentationData.strings.BoostGift_GiveawayCreated_Text let tooltipController = UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: title, text: text, customUndoText: nil, timeout: nil, linkAction: { [weak navigationController] _ in - let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.StatsDatacenterId(id: peerId)) - |> deliverOnMainQueue).startStandalone(next: { [weak navigationController] statsDatacenterId in - guard let statsDatacenterId else { - return - } - let statsController = context.sharedContext.makeChannelStatsController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, boosts: true, boostStatus: nil, statsDatacenterId: statsDatacenterId) - navigationController?.pushViewController(statsController) - }) + let statsController = context.sharedContext.makeChannelStatsController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, boosts: true, boostStatus: nil) + navigationController?.pushViewController(statsController) }), elevatedLayout: false, action: { _ in return true }) diff --git a/submodules/PremiumUI/Sources/DataRainView.swift b/submodules/PremiumUI/Sources/DataRainView.swift index a6ac8ed2593..3aad07bb0d2 100644 --- a/submodules/PremiumUI/Sources/DataRainView.swift +++ b/submodules/PremiumUI/Sources/DataRainView.swift @@ -87,7 +87,7 @@ public final class MatrixView: MTKView, MTKViewDelegate, PhoneDemoDecorationView self.framebufferOnly = true - self.displayLink = SharedDisplayLinkDriver.shared.add { [weak self] in + self.displayLink = SharedDisplayLinkDriver.shared.add { [weak self] _ in self?.tick() } self.displayLink?.isPaused = true diff --git a/submodules/PremiumUI/Sources/GiftOptionItem.swift b/submodules/PremiumUI/Sources/GiftOptionItem.swift index b7fe236e663..0ff43e8df6e 100644 --- a/submodules/PremiumUI/Sources/GiftOptionItem.swift +++ b/submodules/PremiumUI/Sources/GiftOptionItem.swift @@ -567,6 +567,15 @@ class GiftOptionItemNode: ItemListRevealOptionsItemNode { transition.updateFrame(node: strongSelf.labelNode, frame: labelFrame) } else { transition.updateFrame(node: strongSelf.labelNode, frame: CGRect(origin: CGPoint(x: layoutSize.width - rightInset - labelLayout.size.width - 18.0, y: floorToScreenPixels((layout.contentSize.height - labelLayout.size.height) / 2.0)), size: labelLayout.size)) + + if let labelIconNode = strongSelf.labelIconNode { + strongSelf.labelIconNode = nil + labelIconNode.removeFromSupernode() + } + if let labelBackgroundNode = strongSelf.labelBackgroundNode { + strongSelf.labelBackgroundNode = nil + labelBackgroundNode.removeFromSupernode() + } } if item.subtitleActive { diff --git a/submodules/PremiumUI/Sources/LimitsPageComponent.swift b/submodules/PremiumUI/Sources/LimitsPageComponent.swift index bc57d039c33..55c909ca8a6 100644 --- a/submodules/PremiumUI/Sources/LimitsPageComponent.swift +++ b/submodules/PremiumUI/Sources/LimitsPageComponent.swift @@ -198,29 +198,32 @@ private enum Limit: CaseIterable { case folders case chatsPerFolder case account + case recommendedChannels func title(strings: PresentationStrings) -> String { switch self { - case .groups: - return strings.Premium_Limits_GroupsAndChannels - case .pins: - return strings.Premium_Limits_PinnedChats - case .publicLinks: - return strings.Premium_Limits_PublicLinks - case .savedGifs: - return strings.Premium_Limits_SavedGifs - case .favedStickers: - return strings.Premium_Limits_FavedStickers - case .about: - return strings.Premium_Limits_Bio - case .captions: - return strings.Premium_Limits_Captions - case .folders: - return strings.Premium_Limits_Folders - case .chatsPerFolder: - return strings.Premium_Limits_ChatsPerFolder - case .account: - return strings.Premium_Limits_Accounts + case .groups: + return strings.Premium_Limits_GroupsAndChannels + case .pins: + return strings.Premium_Limits_PinnedChats + case .publicLinks: + return strings.Premium_Limits_PublicLinks + case .savedGifs: + return strings.Premium_Limits_SavedGifs + case .favedStickers: + return strings.Premium_Limits_FavedStickers + case .about: + return strings.Premium_Limits_Bio + case .captions: + return strings.Premium_Limits_Captions + case .folders: + return strings.Premium_Limits_Folders + case .chatsPerFolder: + return strings.Premium_Limits_ChatsPerFolder + case .account: + return strings.Premium_Limits_Accounts + case .recommendedChannels: + return strings.Premium_Limits_RecommendedChannels } } @@ -246,6 +249,8 @@ private enum Limit: CaseIterable { return strings.Premium_Limits_ChatsPerFolderInfo case .account: return strings.Premium_Limits_AccountsInfo + case .recommendedChannels: + return strings.Premium_Limits_RecommendedChannelsInfo } } @@ -272,6 +277,8 @@ private enum Limit: CaseIterable { value = configuration.maxFolderChatsCount case .account: value = isPremium ? 4 : 3 + case .recommendedChannels: + value = configuration.maxChannelRecommendationsCount } return "\(value)" } @@ -360,7 +367,8 @@ private final class LimitsListComponent: CombinedComponent { UIColor(rgb: 0xdb5887), UIColor(rgb: 0xdb496f), UIColor(rgb: 0xe95d44), - UIColor(rgb: 0xf2822a) + UIColor(rgb: 0xf2822a), + UIColor(rgb: 0xfdb529) ] let items: [AnyComponentWithIdentity] = Limit.allCases.enumerated().map { index, value in diff --git a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift index a29670279be..575aa94ab46 100644 --- a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift @@ -477,7 +477,7 @@ private final class DemoSheetContent: CombinedComponent { self.context = context self.subject = subject self.source = source - self.order = order ?? [.moreUpload, .fasterDownload, .voiceToText, .noAds, .uniqueReactions, .premiumStickers, .animatedEmoji, .advancedChatManagement, .profileBadge, .animatedUserpics, .appIcons, .translation, .stories] + self.order = order ?? [.moreUpload, .fasterDownload, .voiceToText, .noAds, .uniqueReactions, .premiumStickers, .animatedEmoji, .advancedChatManagement, .profileBadge, .animatedUserpics, .appIcons, .translation, .stories, .colors, .wallpapers] self.action = action self.dismiss = dismiss } @@ -907,7 +907,6 @@ private final class DemoSheetContent: CombinedComponent { ) ) ) - availableItems[.animatedEmoji] = DemoPagerComponent.Item( AnyComponentWithIdentity( id: PremiumDemoScreen.Subject.animatedEmoji, @@ -926,7 +925,6 @@ private final class DemoSheetContent: CombinedComponent { ) ) ) - availableItems[.translation] = DemoPagerComponent.Item( AnyComponentWithIdentity( id: PremiumDemoScreen.Subject.translation, @@ -946,6 +944,43 @@ private final class DemoSheetContent: CombinedComponent { ) ) ) + availableItems[.colors] = DemoPagerComponent.Item( + AnyComponentWithIdentity( + id: PremiumDemoScreen.Subject.colors, + component: AnyComponent( + PageComponent( + content: AnyComponent(PhoneDemoComponent( + context: component.context, + position: .top, + videoFile: configuration.videos["peer_colors"], + decoration: .badgeStars + )), + title: strings.Premium_Colors, + text: strings.Premium_ColorsInfo, + textColor: textColor + ) + ) + ) + ) + availableItems[.wallpapers] = DemoPagerComponent.Item( + AnyComponentWithIdentity( + id: PremiumDemoScreen.Subject.wallpapers, + component: AnyComponent( + PageComponent( + content: AnyComponent(PhoneDemoComponent( + context: component.context, + position: .top, + model: .island, + videoFile: configuration.videos["wallpapers"], + decoration: .swirlStars + )), + title: strings.Premium_Wallpapers, + text: strings.Premium_WallpapersInfo, + textColor: textColor + ) + ) + ) + ) var items: [DemoPagerComponent.Item] = component.order.compactMap { availableItems[$0] } let index: Int @@ -1042,6 +1077,12 @@ private final class DemoSheetContent: CombinedComponent { case .stories: buttonText = strings.Common_OK buttonAnimationName = "premium_unlock" + case .voiceToText: + buttonText = strings.Premium_VoiceToText_Proceed + case .wallpapers: + buttonText = strings.Premium_Wallpaper_Proceed + case .colors: + buttonText = strings.Premium_Colors_Proceed default: buttonText = strings.Common_OK } @@ -1073,6 +1114,10 @@ private final class DemoSheetContent: CombinedComponent { text = strings.Premium_AnimatedEmojiStandaloneInfo case .translation: text = strings.Premium_TranslationStandaloneInfo + case .colors: + text = strings.Premium_ColorsInfo + case .wallpapers: + text = strings.Premium_WallpapersInfo case .doubleLimits: text = "" case .stories: @@ -1291,6 +1336,8 @@ public class PremiumDemoScreen: ViewControllerComponentContainer { case emojiStatus case translation case stories + case colors + case wallpapers } public enum Source: Equatable { diff --git a/submodules/PremiumUI/Sources/PremiumGiftCodeScreen.swift b/submodules/PremiumUI/Sources/PremiumGiftCodeScreen.swift index 8b3f6a39022..36236cfefe2 100644 --- a/submodules/PremiumUI/Sources/PremiumGiftCodeScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumGiftCodeScreen.swift @@ -228,7 +228,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent { additionalText = "" } buttonText = strings.Common_OK - if boost.flags.contains(.isUnclaimed), let slug = boost.slug { + if let slug = boost.slug { link = "https://t.me/giftcode/\(slug)" } else { link = nil diff --git a/submodules/PremiumUI/Sources/PremiumGiftScreen.swift b/submodules/PremiumUI/Sources/PremiumGiftScreen.swift index 6f3d2682e14..88aacf28a88 100644 --- a/submodules/PremiumUI/Sources/PremiumGiftScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumGiftScreen.swift @@ -328,21 +328,23 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent { state.price = price let gradientColors: [UIColor] = [ - UIColor(rgb: 0xF27C30), - UIColor(rgb: 0xE36850), - UIColor(rgb: 0xda5d63), - UIColor(rgb: 0xD15078), - UIColor(rgb: 0xC14998), - UIColor(rgb: 0xB24CB5), - UIColor(rgb: 0xA34ED0), - UIColor(rgb: 0x9054E9), - UIColor(rgb: 0x7561EB), - UIColor(rgb: 0x5A6EEE), - UIColor(rgb: 0x548DFF), - UIColor(rgb: 0x54A3FF), - UIColor(rgb: 0x54bdff), - UIColor(rgb: 0x71c8ff), - UIColor(rgb: 0xa0daff) + UIColor(rgb: 0xef6922), + UIColor(rgb: 0xe95a2c), + UIColor(rgb: 0xe74e33), + UIColor(rgb: 0xe3433c), + UIColor(rgb: 0xdb374b), + UIColor(rgb: 0xcb3e6d), + UIColor(rgb: 0xbc4395), + UIColor(rgb: 0xab4ac4), + UIColor(rgb: 0x9b4fed), + UIColor(rgb: 0x8958ff), + UIColor(rgb: 0x676bff), + UIColor(rgb: 0x5b79ff), + UIColor(rgb: 0x4492ff), + UIColor(rgb: 0x429bd5), + UIColor(rgb: 0x41a6a5), + UIColor(rgb: 0x3eb26d), + UIColor(rgb: 0x3dbd4a) ] i = 0 @@ -401,6 +403,10 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent { demoSubject = .translation case .stories: demoSubject = .stories + case .colors: + demoSubject = .colors + case .wallpapers: + demoSubject = .wallpapers } let buttonText: String diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index ca5d25c1e4d..fe83c075c6f 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -243,6 +243,18 @@ public enum PremiumSource: Equatable { } else { return false } + case .similarChannels: + if case .similarChannels = rhs { + return true + } else { + return false + } + case .wallpapers: + if case .wallpapers = rhs { + return true + } else { + return false + } } } @@ -281,6 +293,8 @@ public enum PremiumSource: Equatable { case storiesSuggestedReactions case channelBoost(EnginePeer.Id) case nameColor + case similarChannels + case wallpapers var identifier: String? { switch self { @@ -356,6 +370,10 @@ public enum PremiumSource: Equatable { return "channel_boost__\(peerId.id._internalGetInt64Value())" case .nameColor: return "name_color" + case .similarChannels: + return "similar_channels" + case .wallpapers: + return "wallpapers" } } } @@ -376,6 +394,8 @@ public enum PremiumPerk: CaseIterable { case emojiStatus case translation case stories + case colors + case wallpapers public static var allCases: [PremiumPerk] { return [ @@ -393,7 +413,9 @@ public enum PremiumPerk: CaseIterable { .animatedEmoji, .emojiStatus, .translation, - .stories + .stories, + .colors, + .wallpapers ] } @@ -439,6 +461,10 @@ public enum PremiumPerk: CaseIterable { return "translations" case .stories: return "stories" + case .colors: + return "peer_colors" + case .wallpapers: + return "wallpapers" } } @@ -474,6 +500,10 @@ public enum PremiumPerk: CaseIterable { return strings.Premium_Translation case .stories: return strings.Premium_Stories + case .colors: + return strings.Premium_Colors + case .wallpapers: + return strings.Premium_Wallpapers } } @@ -509,6 +539,10 @@ public enum PremiumPerk: CaseIterable { return strings.Premium_TranslationInfo case .stories: return strings.Premium_StoriesInfo + case .colors: + return strings.Premium_ColorsInfo + case .wallpapers: + return strings.Premium_WallpapersInfo } } @@ -544,6 +578,10 @@ public enum PremiumPerk: CaseIterable { return "Premium/Perk/Translation" case .stories: return "Premium/Perk/Stories" + case .colors: + return "Premium/Perk/Colors" + case .wallpapers: + return "Premium/Perk/Wallpapers" } } } @@ -559,6 +597,8 @@ struct PremiumIntroConfiguration { .voiceToText, .noAds, .emojiStatus, + .colors, + .wallpapers, .uniqueReactions, .premiumStickers, .animatedEmoji, @@ -594,6 +634,14 @@ struct PremiumIntroConfiguration { if perks.count < 4 { perks = PremiumIntroConfiguration.defaultValue.perks } + #if DEBUG + if !perks.contains(.wallpapers) { + perks.append(.wallpapers) + } + if !perks.contains(.colors) { + perks.append(.colors) + } + #endif return PremiumIntroConfiguration(perks: perks) } else { return .defaultValue @@ -1319,7 +1367,7 @@ final class PerkComponent: CombinedComponent { let badgeWidth = badgeText.size.width + 7.0 let badgeBackground = badgeBackground.update( component: RoundedRectangle( - colors: [component.accentColor], + colors: component.iconBackgroundColors, cornerRadius: 5.0, gradientDirection: .vertical), availableSize: CGSize(width: badgeWidth, height: 16.0), @@ -1530,12 +1578,21 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { }) - self.newPerksDisposable = (ApplicationSpecificNotice.dismissedPremiumAppIconsBadge(accountManager: context.sharedContext.accountManager) - |> deliverOnMainQueue).startStrict(next: { [weak self] dismissedPremiumAppIconsBadge in + self.newPerksDisposable = combineLatest(queue: Queue.mainQueue(), + ApplicationSpecificNotice.dismissedPremiumAppIconsBadge(accountManager: context.sharedContext.accountManager), + ApplicationSpecificNotice.dismissedPremiumWallpapersBadge(accountManager: context.sharedContext.accountManager), + ApplicationSpecificNotice.dismissedPremiumColorsBadge(accountManager: context.sharedContext.accountManager) + ).startStrict(next: { [weak self] dismissedPremiumAppIconsBadge, dismissedPremiumWallpapersBadge, dismissedPremiumColorsBadge in guard let self else { return } - let newPerks: [String] = [] + var newPerks: [String] = [] + if !dismissedPremiumWallpapersBadge { + newPerks.append(PremiumPerk.wallpapers.identifier) + } + if !dismissedPremiumColorsBadge { + newPerks.append(PremiumPerk.colors.identifier) + } self.newPerks = newPerks self.updated() }) @@ -1687,21 +1744,23 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { // let gradientColors: [UIColor] = [ - UIColor(rgb: 0xF27C30), - UIColor(rgb: 0xE36850), - UIColor(rgb: 0xda5d63), - UIColor(rgb: 0xD15078), - UIColor(rgb: 0xC14998), - UIColor(rgb: 0xB24CB5), - UIColor(rgb: 0xA34ED0), - UIColor(rgb: 0x9054E9), - UIColor(rgb: 0x7561EB), - UIColor(rgb: 0x5A6EEE), - UIColor(rgb: 0x548DFF), - UIColor(rgb: 0x54A3FF), - UIColor(rgb: 0x54bdff), - UIColor(rgb: 0x71c8ff), - UIColor(rgb: 0xa0daff) + UIColor(rgb: 0xef6922), + UIColor(rgb: 0xe95a2c), + UIColor(rgb: 0xe74e33), + UIColor(rgb: 0xe3433c), + UIColor(rgb: 0xdb374b), + UIColor(rgb: 0xcb3e6d), + UIColor(rgb: 0xbc4395), + UIColor(rgb: 0xab4ac4), + UIColor(rgb: 0x9b4fed), + UIColor(rgb: 0x8958ff), + UIColor(rgb: 0x676bff), + UIColor(rgb: 0x5b79ff), + UIColor(rgb: 0x4492ff), + UIColor(rgb: 0x429bd5), + UIColor(rgb: 0x41a6a5), + UIColor(rgb: 0x3eb26d), + UIColor(rgb: 0x3dbd4a) ] let accountContext = context.component.context @@ -1888,6 +1947,12 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { demoSubject = .translation case .stories: demoSubject = .stories + case .colors: + demoSubject = .colors + let _ = ApplicationSpecificNotice.setDismissedPremiumColorsBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone() + case .wallpapers: + demoSubject = .wallpapers + let _ = ApplicationSpecificNotice.setDismissedPremiumWallpapersBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone() } let isPremium = state?.isPremium == true @@ -2060,7 +2125,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { peer, navigation in }, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { [weak controller] c, arguments in controller?.push(c) - }, dismissInput: {}, contentContext: nil, progress: nil) + }, dismissInput: {}, contentContext: nil, progress: nil, completion: nil) }) } } diff --git a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift index 60b99f9dae2..29573caeb3d 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift @@ -1191,7 +1191,10 @@ private final class LimitSheetContent: CombinedComponent { } case .nameColors: titleText = strings.ChannelBoost_EnableColors - string = strings.ChannelBoost_EnableColorsText(valueString).string + string = strings.ChannelBoost_EnableColorsLevelText("\(premiumConfiguration.minChannelNameColorLevel)").string + case let .channelReactions(reactionCount): + titleText = strings.ChannelBoost_CustomReactions + string = strings.ChannelBoost_CustomReactionsText("\(reactionCount)", "\(reactionCount)").string } } else { let storiesString = strings.ChannelBoost_StoriesPerDay(level) @@ -1773,11 +1776,12 @@ public class PremiumLimitScreen: ViewControllerComponentContainer { case storiesWeekly case storiesMonthly - - public enum BoostSubject { + public enum BoostSubject: Equatable { case stories case nameColors + case channelReactions(reactionCount: Int) } + case storiesChannelBoost(peer: EnginePeer, boostSubject: BoostSubject, isCurrent: Bool, level: Int32, currentLevelBoosts: Int32, nextLevelBoosts: Int32?, link: String?, myBoostCount: Int32, canBoostAgain: Bool) } diff --git a/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift index 9e6ffe1660f..43617b719f5 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift @@ -668,7 +668,6 @@ public class PremiumLimitsListScreen: ViewController { ) ) ) - availableItems[.animatedEmoji] = DemoPagerComponent.Item( AnyComponentWithIdentity( id: PremiumDemoScreen.Subject.animatedEmoji, @@ -687,7 +686,6 @@ public class PremiumLimitsListScreen: ViewController { ) ) ) - availableItems[.translation] = DemoPagerComponent.Item( AnyComponentWithIdentity( id: PremiumDemoScreen.Subject.translation, @@ -707,6 +705,43 @@ public class PremiumLimitsListScreen: ViewController { ) ) ) + availableItems[.colors] = DemoPagerComponent.Item( + AnyComponentWithIdentity( + id: PremiumDemoScreen.Subject.colors, + component: AnyComponent( + PageComponent( + content: AnyComponent(PhoneDemoComponent( + context: context, + position: .top, + videoFile: configuration.videos["peer_colors"], + decoration: .badgeStars + )), + title: strings.Premium_Colors, + text: strings.Premium_ColorsInfo, + textColor: textColor + ) + ) + ) + ) + availableItems[.wallpapers] = DemoPagerComponent.Item( + AnyComponentWithIdentity( + id: PremiumDemoScreen.Subject.wallpapers, + component: AnyComponent( + PageComponent( + content: AnyComponent(PhoneDemoComponent( + context: context, + position: .top, + model: .island, + videoFile: configuration.videos["wallpapers"], + decoration: .swirlStars + )), + title: strings.Premium_Wallpapers, + text: strings.Premium_WallpapersInfo, + textColor: textColor + ) + ) + ) + ) if let order = controller.order { var items: [DemoPagerComponent.Item] = order.compactMap { availableItems[$0] } diff --git a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift index 898c6383a51..7ec0009c5e8 100644 --- a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift +++ b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift @@ -513,7 +513,7 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie self.errorTextNode.textAlignment = .center self.errorTextNode.isHidden = true - self.camera = Camera(configuration: .init(preset: .hd1920x1080, position: .back, audio: false, photo: true, metadata: true, preferredFps: 60), previewView: self.previewView) + self.camera = Camera(configuration: .init(preset: .hd1920x1080, position: .back, audio: false, photo: true, metadata: true), previewView: self.previewView) super.init() @@ -916,7 +916,7 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie self?.controller?.present(c, in: .window(.root), with: a) }, dismissInput: { [weak self] in self?.view.endEditing(true) - }, contentContext: nil, progress: nil) + }, contentContext: nil, progress: nil, completion: nil) return true } diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index d7d088dbdd3..a61a4d9ec11 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -296,6 +296,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { private var extensionDistance: CGFloat = 0.0 public private(set) var visibleExtensionDistance: CGFloat = 0.0 + private var emojiContentHeight: CGFloat = 300.0 + private var didInitializeEmojiContentHeight: Bool = false private var emojiContentLayout: EmojiPagerContentComponent.CustomLayout? private var emojiContent: EmojiPagerContentComponent? private var scheduledEmojiContentAnimationHint: EmojiPagerContentComponent.ContentAnimation? @@ -322,6 +324,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { private var availableReactions: AvailableReactions? private var availableReactionsDisposable: Disposable? + public let alwaysAllowPremiumReactions: Bool private var hasPremium: Bool? private var hasPremiumDisposable: Disposable? @@ -368,7 +371,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } } - public init(context: AccountContext, animationCache: AnimationCache, presentationData: PresentationData, items: [ReactionContextItem], selectedItems: Set, title: String? = nil, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?, isExpandedUpdated: @escaping (ContainedViewLayoutTransition) -> Void, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, requestUpdateOverlayWantsToBeBelowKeyboard: @escaping (ContainedViewLayoutTransition) -> Void) { + public init(context: AccountContext, animationCache: AnimationCache, presentationData: PresentationData, items: [ReactionContextItem], selectedItems: Set, title: String? = nil, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?, isExpandedUpdated: @escaping (ContainedViewLayoutTransition) -> Void, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, requestUpdateOverlayWantsToBeBelowKeyboard: @escaping (ContainedViewLayoutTransition) -> Void) { self.context = context self.presentationData = presentationData self.items = items @@ -468,6 +471,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { self.contentTopInset = 24.0 } + self.alwaysAllowPremiumReactions = alwaysAllowPremiumReactions + super.init() self.addSubnode(self.backgroundNode) @@ -486,13 +491,17 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { strongSelf.availableReactions = availableReactions }) - self.hasPremiumDisposable = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) - |> deliverOnMainQueue).start(next: { [weak self] peer in - guard let strongSelf = self else { - return - } - strongSelf.hasPremium = peer?.isPremium ?? false - }) + if alwaysAllowPremiumReactions { + self.hasPremium = true + } else { + self.hasPremiumDisposable = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) + |> deliverOnMainQueue).start(next: { [weak self] peer in + guard let strongSelf = self else { + return + } + strongSelf.hasPremium = peer?.isPremium ?? false + }) + } if let getEmojiContent = getEmojiContent { let viewKey = PostboxViewKey.orderedItemList(id: Namespaces.OrderedItemList.CloudFeaturedEmojiPacks) @@ -566,6 +575,13 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { emojiTransition = Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(contentAnimation) } + var hideTopPanel = false + if strongSelf.isReactionSearchActive { + hideTopPanel = true + } else if strongSelf.alwaysAllowPremiumReactions { + hideTopPanel = true + } + let _ = reactionSelectionComponentHost.update( transition: emojiTransition, component: AnyComponent(EmojiStatusSelectionComponent( @@ -575,7 +591,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { emojiContent: emojiContent, backgroundColor: .clear, separatorColor: strongSelf.presentationData.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5), - hideTopPanel: strongSelf.isReactionSearchActive, + hideTopPanel: hideTopPanel, + disableTopPanel: strongSelf.alwaysAllowPremiumReactions, hideTopPanelUpdated: { hideTopPanel, transition in guard let strongSelf = self else { return @@ -585,7 +602,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } )), environment: {}, - containerSize: CGSize(width: componentView.bounds.width, height: 300.0) + containerSize: CGSize(width: componentView.bounds.width, height: strongSelf.emojiContentHeight) ) } }) @@ -1023,7 +1040,13 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { expandItemSize = 30.0 expandTintOffset = 0.0 } - let baseNextFrame = CGRect(origin: CGPoint(x: self.scrollNode.view.bounds.width - expandItemSize - 9.0, y: self.contentTopInset + containerHeight - contentHeight + floor((contentHeight - expandItemSize) / 2.0) + (self.isExpanded ? (46.0 + 54.0 - 4.0) : 0.0)), size: CGSize(width: expandItemSize, height: expandItemSize + self.extensionDistance)) + var baseNextFrame = CGRect(origin: CGPoint(x: self.scrollNode.view.bounds.width - expandItemSize - 9.0, y: self.contentTopInset + containerHeight - contentHeight + floor((contentHeight - expandItemSize) / 2.0)), size: CGSize(width: expandItemSize, height: expandItemSize + self.extensionDistance)) + if self.isExpanded { + if self.alwaysAllowPremiumReactions { + } else { + baseNextFrame.origin.y += 46.0 + 54.0 - 4.0 + } + } transition.updateFrame(view: expandItemView, frame: baseNextFrame) transition.updateFrame(view: expandItemView.tintView, frame: baseNextFrame.offsetBy(dx: 0.0, dy: expandTintOffset)) @@ -1124,7 +1147,16 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { visibleItemCount: itemCount ) - var scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: self.isExpanded ? (46.0 + 54.0 - 4.0) : self.contentTopInset), size: actualBackgroundFrame.size) + var scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: actualBackgroundFrame.size) + if self.isExpanded { + if self.alwaysAllowPremiumReactions { + scrollFrame.origin.y += 0.0 + } else { + scrollFrame.origin.y += 46.0 + 54.0 - 4.0 + } + } else { + scrollFrame.origin.y += self.contentTopInset + } scrollFrame.origin.y += floorToScreenPixels(self.extensionDistance / 2.0) transition.updatePosition(node: self.contentContainer, position: visualBackgroundFrame.center, beginWithCurrentState: true) @@ -1142,6 +1174,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { self.updateScrolling(transition: transition) self.emojiContentLayout = EmojiPagerContentComponent.CustomLayout( + topPanelAlwaysHidden: self.alwaysAllowPremiumReactions, itemsPerRow: itemCount, itemSize: itemSize, sideInset: sideInset, @@ -1168,6 +1201,13 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { componentTransition = Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(contentAnimation) } + var hideTopPanel = false + if self.isReactionSearchActive { + hideTopPanel = true + } else if self.alwaysAllowPremiumReactions { + hideTopPanel = true + } + let _ = reactionSelectionComponentHost.update( transition: componentTransition, component: AnyComponent(EmojiStatusSelectionComponent( @@ -1177,7 +1217,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { emojiContent: emojiContent, backgroundColor: .clear, separatorColor: self.presentationData.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5), - hideTopPanel: self.isReactionSearchActive, + hideTopPanel: hideTopPanel, + disableTopPanel: self.alwaysAllowPremiumReactions, hideTopPanelUpdated: { [weak self] hideTopPanel, transition in guard let strongSelf = self else { return @@ -1187,7 +1228,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } )), environment: {}, - containerSize: CGSize(width: actualBackgroundFrame.width, height: 300.0) + containerSize: CGSize(width: actualBackgroundFrame.width, height: self.emojiContentHeight) ) if let componentView = reactionSelectionComponentHost.view { var animateIn = false @@ -1229,7 +1270,15 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { if let mirrorContentClippingView = emojiView.mirrorContentClippingView { mirrorContentClippingView.clipsToBounds = false - Transition(transition).animateBoundsOrigin(view: mirrorContentClippingView, from: CGPoint(x: 0.0, y: 46.0 + 54.0 - 4.0), to: CGPoint(), additive: true, completion: { [weak mirrorContentClippingView] _ in + + var animationOffsetY: CGFloat = 0.0 + if self.alwaysAllowPremiumReactions { + animationOffsetY += -4.0 + } else { + animationOffsetY += 46.0 + 54.0 - 4.0 + } + + Transition(transition).animateBoundsOrigin(view: mirrorContentClippingView, from: CGPoint(x: 0.0, y: animationOffsetY), to: CGPoint(), additive: true, completion: { [weak mirrorContentClippingView] _ in mirrorContentClippingView?.clipsToBounds = true }) } @@ -1260,7 +1309,14 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { componentTransition.setFrame(view: componentView, frame: CGRect(origin: componentFrame.origin, size: CGSize(width: componentFrame.width, height: componentFrame.height))) if animateIn { - transition.animatePositionAdditive(layer: componentView.layer, offset: CGPoint(x: 0.0, y: -(46.0 + 54.0 - 4.0) + floorToScreenPixels(self.animateFromExtensionDistance / 2.0))) + var animationOffsetY: CGFloat = 0.0 + if self.alwaysAllowPremiumReactions { + animationOffsetY += 4.0 + } else { + animationOffsetY += 46.0 + 54.0 - 4.0 + } + + transition.animatePositionAdditive(layer: componentView.layer, offset: CGPoint(x: 0.0, y: -animationOffsetY + floorToScreenPixels(self.animateFromExtensionDistance / 2.0))) } } } @@ -1317,6 +1373,17 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { return } + if !self.didInitializeEmojiContentHeight { + self.didInitializeEmojiContentHeight = true + + if emojiContent.contentItemGroups.count == 1 { + let itemCount = emojiContent.contentItemGroups[0].items.count + let numRows = (itemCount + (emojiContentLayout.itemsPerRow - 1)) / emojiContentLayout.itemsPerRow + let proposedHeight: CGFloat = CGFloat(numRows) * emojiContentLayout.itemSize + CGFloat(numRows - 1) * emojiContentLayout.itemSpacing + emojiContentLayout.itemSpacing * 2.0 + 5.0 + self.emojiContentHeight = min(300.0, proposedHeight) + } + } + emojiContent.inputInteractionHolder.inputInteraction = EmojiPagerContentComponent.InputInteraction( performItemAction: { [weak self] groupId, item, sourceView, sourceRect, sourceLayer, isLongPress in guard let strongSelf = self, let availableReactions = strongSelf.availableReactions, let itemFile = item.itemFile else { @@ -1502,15 +1569,21 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } |> distinctUntilChanged + let remotePacksSignal: Signal<(sets: FoundStickerSets, isFinalResult: Bool), NoError> = .single((FoundStickerSets(), false)) |> then( + context.engine.stickers.searchEmojiSetsRemotely(query: query) |> map { + ($0, true) + } + ) + let resultSignal = signal |> mapToSignal { keywords -> Signal<[EmojiPagerContentComponent.ItemGroup], NoError> in return combineLatest( - context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000), - context.engine.stickers.availableReactions(), - hasPremium + context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000) |> take(1), + context.engine.stickers.availableReactions() |> take(1), + hasPremium |> take(1), + remotePacksSignal ) - |> take(1) - |> map { view, availableReactions, hasPremium -> [EmojiPagerContentComponent.ItemGroup] in + |> map { view, availableReactions, hasPremium, foundPacks -> [EmojiPagerContentComponent.ItemGroup] in var result: [(String, TelegramMediaFile?, String)] = [] var allEmoticons: [String: String] = [:] @@ -1561,7 +1634,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } } - return [EmojiPagerContentComponent.ItemGroup( + var resultGroups: [EmojiPagerContentComponent.ItemGroup] = [] + resultGroups.append(EmojiPagerContentComponent.ItemGroup( supergroupId: "search", groupId: "search", title: nil, @@ -1576,7 +1650,59 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { headerItem: nil, fillWithLoadingPlaceholders: false, items: items - )] + )) + + for (collectionId, info, _, _) in foundPacks.sets.infos { + if let info = info as? StickerPackCollectionInfo { + var topItems: [StickerPackItem] = [] + for e in foundPacks.sets.entries { + if let item = e.item as? StickerPackItem { + if e.index.collectionId == collectionId { + topItems.append(item) + } + } + } + + var groupItems: [EmojiPagerContentComponent.Item] = [] + for item in topItems { + var tintMode: EmojiPagerContentComponent.Item.TintMode = .none + if item.file.isCustomTemplateEmoji { + tintMode = .primary + } + + let animationData = EntityKeyboardAnimationData(file: item.file) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: item.file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + groupItems.append(resultItem) + } + + resultGroups.append(EmojiPagerContentComponent.ItemGroup( + supergroupId: AnyHashable(info.id), + groupId: AnyHashable(info.id), + title: info.title, + subtitle: nil, + actionButtonTitle: nil, + isFeatured: false, + isPremiumLocked: false, + isEmbedded: false, + hasClear: false, + collapsedLineCount: 3, + displayPremiumBadges: false, + headerItem: nil, + fillWithLoadingPlaceholders: false, + items: groupItems + )) + } + } + + return resultGroups } } @@ -2288,7 +2414,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { if let expandItemView = self.expandItemView, expandItemView.bounds.contains(self.view.convert(point, to: self.expandItemView)) { self.animateFromExtensionDistance = self.contentTopInset * 2.0 + self.extensionDistance self.contentTopInset = 0.0 - self.currentContentHeight = 300.0 + self.currentContentHeight = self.emojiContentHeight self.isExpanded = true self.longPressRecognizer?.isEnabled = false self.isExpandedUpdated(.animated(duration: 0.4, curve: .spring)) @@ -2329,7 +2455,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { self.extensionDistance = 0.0 self.visibleExtensionDistance = 0.0 self.contentTopInset = 0.0 - self.currentContentHeight = 300.0 + self.currentContentHeight = self.emojiContentHeight self.isExpanded = true self.isExpandedUpdated(.animated(duration: 0.4, curve: .spring)) } diff --git a/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift b/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift index 89ef9f128fb..4d01e62b3d3 100644 --- a/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift +++ b/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift @@ -119,7 +119,6 @@ public final class SelectablePeerNode: ASDisplayNode { self.avatarNode = AvatarNode(font: avatarFont) self.avatarNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 60.0, height: 60.0)) - self.avatarNode.isLayerBacked = !smartInvertColorsEnabled() self.textNode = ImmediateTextNode() self.textNode.isUserInteractionEnabled = false @@ -171,6 +170,16 @@ public final class SelectablePeerNode: ASDisplayNode { ) } + public func setupStoryRepost(accountPeerId: EnginePeer.Id, postbox: Postbox, network: Network, theme: PresentationTheme, strings: PresentationStrings, synchronousLoad: Bool) { + self.peer = nil + + self.textNode.maximumNumberOfLines = 2 + self.textNode.attributedText = NSAttributedString(string: strings.Share_RepostStory, font: textFont, textColor: self.theme.textColor, paragraphAlignment: .center) + self.avatarNode.setPeer(accountPeerId: accountPeerId, postbox: postbox, network: network, contentSettings: ContentSettings.default, theme: theme, peer: nil, overrideImage: .repostIcon, emptyColor: self.theme.avatarPlaceholderColor, clipStyle: .round, synchronousLoad: synchronousLoad) + + self.avatarNode.playRepostAnimation() + } + public func setup(accountPeerId: EnginePeer.Id, postbox: Postbox, network: Network, energyUsageSettings: EnergyUsageSettings, contentSettings: ContentSettings, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, resolveInlineStickers: @escaping ([Int64]) -> Signal<[Int64: TelegramMediaFile], NoError>, theme: PresentationTheme, strings: PresentationStrings, peer: EngineRenderedPeer, customTitle: String? = nil, iconId: Int64? = nil, iconColor: Int32? = nil, online: Bool = false, numberOfLines: Int = 2, synchronousLoad: Bool) { let isFirstTime = self.peer == nil self.peer = peer diff --git a/submodules/SettingsUI/BUILD b/submodules/SettingsUI/BUILD index 924374f2509..6bf6353f3d3 100644 --- a/submodules/SettingsUI/BUILD +++ b/submodules/SettingsUI/BUILD @@ -120,6 +120,8 @@ swift_library( "//submodules/AttachmentUI:AttachmentUI", "//submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen", "//submodules/TelegramUI/Components/Settings/PeerNameColorScreen", + "//submodules/ManagedAnimationNode:ManagedAnimationNode", + "//submodules/TelegramUI/Components/Settings/QuickReactionSetupController", ], visibility = [ "//visibility:public", diff --git a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift index b549d3d41e8..9dd7708f93e 100644 --- a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift +++ b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift @@ -69,7 +69,7 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel self.messagesContainerNode.clipsToBounds = true self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) - self.chatBackgroundNode.update(wallpaper: self.presentationData.chatWallpaper) + self.chatBackgroundNode.update(wallpaper: self.presentationData.chatWallpaper, animated: false) self.chatBackgroundNode.updateBubbleTheme(bubbleTheme: self.presentationData.theme, bubbleCorners: self.presentationData.chatBubbleCorners) self.toolbarNode = BubbleSettingsToolbarNode(presentationThemeSettings: self.presentationThemeSettings, presentationData: self.presentationData) @@ -163,27 +163,27 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel let otherPeerId = self.context.account.peerId var peers = SimpleDictionary() var messages = SimpleDictionary() - peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil) - peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil) + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) + peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false)) let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false)) let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA=" let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: Data(base64Encoded: waveformBase64)!)] let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes) let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false)) let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false)) let width: CGFloat if case .regular = layout.metrics.widthClass { diff --git a/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift b/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift index b2eef15752b..d2ad53873f0 100644 --- a/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift +++ b/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift @@ -28,7 +28,16 @@ public func ChangePhoneNumberController(context: AccountContext) -> ViewControll controller.loginWithNumber = { [weak controller] phoneNumber, _ in controller?.inProgress = true - requestDisposable.set((context.engine.accountData.requestChangeAccountPhoneNumberVerification(phoneNumber: phoneNumber) + let authorizationPushConfiguration = context.sharedContext.authorizationPushConfiguration + |> take(1) + |> timeout(2.0, queue: .mainQueue(), alternate: .single(nil)) + + requestDisposable.set(( + authorizationPushConfiguration + |> castError(RequestChangeAccountPhoneNumberVerificationError.self) + |> mapToSignal { authorizationPushConfiguration in + return context.engine.accountData.requestChangeAccountPhoneNumberVerification(phoneNumber: phoneNumber, pushNotificationConfiguration: authorizationPushConfiguration, firebaseSecretStream: context.sharedContext.firebaseSecretStream) + } |> deliverOnMainQueue).start(next: { [weak controller] next in controller?.inProgress = false @@ -86,6 +95,12 @@ public func ChangePhoneNumberController(context: AccountContext) -> ViewControll } })) } + codeController.requestNextOption = { [weak codeController] in + guard let codeController else { + return + } + AuthorizationSequenceController.presentDidNotGetCodeUI(controller: codeController, presentationData: context.sharedContext.currentPresentationData.with({ $0 }), number: phoneNumber) + } codeController.openFragment = { url in context.sharedContext.applicationBindings.openUrl(url) } diff --git a/submodules/SettingsUI/Sources/DeleteAccountOptionsController.swift b/submodules/SettingsUI/Sources/DeleteAccountOptionsController.swift index 9e41ad7017b..684258ebd4b 100644 --- a/submodules/SettingsUI/Sources/DeleteAccountOptionsController.swift +++ b/submodules/SettingsUI/Sources/DeleteAccountOptionsController.swift @@ -313,7 +313,7 @@ public func deleteAccountOptionsController(context: AccountContext, navigationCo context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { peer, navigation in }, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { controller, arguments in pushControllerImpl?(controller) - }, dismissInput: {}, contentContext: nil, progress: nil) + }, dismissInput: {}, contentContext: nil, progress: nil, completion: nil) }) } @@ -354,7 +354,7 @@ public func deleteAccountOptionsController(context: AccountContext, navigationCo context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { peer, navigation in }, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { controller, arguments in pushControllerImpl?(controller) - }, dismissInput: {}, contentContext: nil, progress: nil) + }, dismissInput: {}, contentContext: nil, progress: nil, completion: nil) }) } diff --git a/submodules/SettingsUI/Sources/LogoutOptionsController.swift b/submodules/SettingsUI/Sources/LogoutOptionsController.swift index 7803ae7327b..a399dab7ec0 100644 --- a/submodules/SettingsUI/Sources/LogoutOptionsController.swift +++ b/submodules/SettingsUI/Sources/LogoutOptionsController.swift @@ -220,7 +220,7 @@ public func logoutOptionsController(context: AccountContext, navigationControlle context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { peer, navigation in }, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { controller, arguments in pushControllerImpl?(controller) - }, dismissInput: {}, contentContext: nil, progress: nil) + }, dismissInput: {}, contentContext: nil, progress: nil, completion: nil) }) } diff --git a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift index 22ab048ed3a..3569643a28e 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift @@ -133,7 +133,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode { return { item, params, neighbors in if currentBackgroundNode == nil { currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false) - currentBackgroundNode?.update(wallpaper: item.wallpaper) + currentBackgroundNode?.update(wallpaper: item.wallpaper, animated: false) currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) } @@ -145,11 +145,11 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode { var peers = SimpleDictionary() let messages = SimpleDictionary() - peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: item.peerName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil) + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: item.peerName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) let forwardInfo = MessageForwardInfo(author: item.linkEnabled ? peers[peerId] : nil, source: nil, sourceMessageId: nil, date: 0, authorSignature: item.linkEnabled ? nil : item.peerName, psaType: nil, flags: []) - let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: forwardInfo, author: nil, text: item.strings.Privacy_Forwards_PreviewMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, isCentered: false) + let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: forwardInfo, author: nil, text: item.strings.Privacy_Forwards_PreviewMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false) var node: ListViewItemNode? if let current = currentNode { @@ -190,7 +190,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode { strongSelf.item = item if let currentBackgroundNode { - currentBackgroundNode.update(wallpaper: item.wallpaper) + currentBackgroundNode.update(wallpaper: item.wallpaper, animated: false) currentBackgroundNode.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) } @@ -268,7 +268,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode { if let backgroundNode = strongSelf.backgroundNode { backgroundNode.frame = backgroundFrame.insetBy(dx: 0.0, dy: -100.0) - backgroundNode.update(wallpaper: item.wallpaper) + backgroundNode.update(wallpaper: item.wallpaper, animated: false) backgroundNode.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) backgroundNode.updateLayout(size: backgroundNode.bounds.size, displayMode: displayMode, transition: .immediate) } diff --git a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift index 9383034a593..a8de19b0b0e 100644 --- a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift +++ b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift @@ -1068,7 +1068,7 @@ func settingsSearchableItems(context: AccountContext, notificationExceptionsList context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { peer, navigation in }, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { controller, arguments in present(.push, controller) - }, dismissInput: {}, contentContext: nil, progress: nil) + }, dismissInput: {}, contentContext: nil, progress: nil, completion: nil) }) }) allItems.append(faq) diff --git a/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift b/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift index 6ec4032274e..72c5e8e88fa 100644 --- a/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift +++ b/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift @@ -18,6 +18,7 @@ import ShareController import WebPBinding import ReactionImageComponent import FeaturedStickersScreen +import QuickReactionSetupController private final class InstalledStickerPacksControllerArguments { let context: AccountContext diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index ca37f1a9d8a..06dad8b58c7 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -89,7 +89,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView self.messagesContainerNode.clipsToBounds = true self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) - self.chatBackgroundNode.update(wallpaper: self.presentationData.chatWallpaper) + self.chatBackgroundNode.update(wallpaper: self.presentationData.chatWallpaper, animated: false) self.chatBackgroundNode.updateBubbleTheme(bubbleTheme: self.presentationData.theme, bubbleCorners: self.presentationData.chatBubbleCorners) self.toolbarNode = TextSelectionToolbarNode(presentationThemeSettings: self.presentationThemeSettings, presentationData: self.presentationData) @@ -304,14 +304,14 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView ) } - 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)) - 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)) - 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)) - 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)) - 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)) - 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)) - 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)) - 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) let timestamp = self.referenceTimestamp @@ -427,27 +427,27 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView let otherPeerId = self.context.account.peerId var peers = SimpleDictionary() var messages = SimpleDictionary() - peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil) - peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil) + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) + peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false)) let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false)) let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA=" let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: Data(base64Encoded: waveformBase64)!)] let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes) let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false)) let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false)) let width: CGFloat if case .regular = layout.metrics.widthClass { diff --git a/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift b/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift index bddf128ca92..821ea25b204 100644 --- a/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift +++ b/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift @@ -25,7 +25,7 @@ func presentCustomWallpaperPicker(context: AccountContext, present: @escaping (V controller.selectionBlock = { [weak legacyController] asset, _ in if let asset = asset { let controller = WallpaperGalleryController(context: context, source: .asset(asset.backingAsset)) - controller.apply = { [weak legacyController, weak controller] wallpaper, mode, editedImage, cropRect, brightness in + controller.apply = { [weak legacyController, weak controller] wallpaper, mode, editedImage, cropRect, brightness, _ in if let legacyController = legacyController, let controller = controller { uploadCustomWallpaper(context: context, wallpaper: wallpaper, mode: mode, editedImage: nil, cropRect: cropRect, brightness: brightness, completion: { [weak legacyController, weak controller] in if let legacyController = legacyController, let controller = controller { @@ -200,7 +200,7 @@ func uploadCustomWallpaper(context: AccountContext, wallpaper: WallpaperGalleryE }).start() } -public func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: WallpaperGalleryEntry, mode: WallpaperPresentationOptions, editedImage: UIImage?, cropRect: CGRect?, brightness: CGFloat?, peerId: PeerId, completion: @escaping () -> Void) { +public func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: WallpaperGalleryEntry, mode: WallpaperPresentationOptions, editedImage: UIImage?, cropRect: CGRect?, brightness: CGFloat?, peerId: PeerId, forBoth: Bool, completion: @escaping () -> Void) { var imageSignal: Signal switch wallpaper { case let .wallpaper(wallpaper, _): @@ -308,7 +308,7 @@ public func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: Wallpa let settings = WallpaperSettings(blur: mode.contains(.blur), motion: mode.contains(.motion), colors: [], intensity: intensity) let temporaryWallpaper: TelegramWallpaper = .image([TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailDimensions), resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false), TelegramMediaImageRepresentation(dimensions: PixelDimensions(croppedImage.size), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)], settings) - context.account.pendingPeerMediaUploadManager.add(peerId: peerId, content: .wallpaper(temporaryWallpaper)) + context.account.pendingPeerMediaUploadManager.add(peerId: peerId, content: .wallpaper(wallpaper: temporaryWallpaper, forBoth: forBoth)) Queue.mainQueue().after(0.05) { completion() diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift index a3c9b719123..8260484768e 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift @@ -175,7 +175,7 @@ final class ThemeAccentColorController: ViewController { } if case let .peer(peer) = strongSelf.resultMode { - let _ = strongSelf.context.engine.themes.setChatWallpaper(peerId: peer.id, wallpaper: coloredWallpaper).start() + let _ = strongSelf.context.engine.themes.setChatWallpaper(peerId: peer.id, wallpaper: coloredWallpaper, forBoth: false).start() strongSelf.completion?() return } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index cdf27920f1d..243bc64bc5d 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -307,8 +307,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate let doneButtonType: WallpaperGalleryToolbarDoneButtonType if case .edit(_, _, _, _, _, true, _) = self.mode { doneButtonType = .proceed - } else if case .peer = resultMode { - doneButtonType = .setPeer + } else if case let .peer(peer) = resultMode { + doneButtonType = .setPeer(peer.compactDisplayTitle, context.isPremium) } else { doneButtonType = .set } @@ -437,7 +437,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate } } - self.toolbarNode.done = { [weak self] in + self.toolbarNode.done = { [weak self] _ in if let strongSelf = self { if strongSelf.state.displayPatternPanel { strongSelf.updateState({ current in @@ -454,7 +454,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate } } - self.backgroundNode.update(wallpaper: self.wallpaper) + self.backgroundNode.update(wallpaper: self.wallpaper, animated: false) self.backgroundNode.updateBubbleTheme(bubbleTheme: self.theme, bubbleCorners: self.presentationData.chatBubbleCorners) self.stateDisposable = (self.statePromise.get() @@ -551,7 +551,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate strongSelf.serviceBackgroundColor = serviceBackgroundColor strongSelf.serviceBackgroundColorPromise.set(.single(serviceBackgroundColor)) - strongSelf.backgroundNode.update(wallpaper: wallpaper) + strongSelf.backgroundNode.update(wallpaper: wallpaper, animated: false) strongSelf.backgroundNode.updateBubbleTheme(bubbleTheme: strongSelf.theme, bubbleCorners: strongSelf.presentationData.chatBubbleCorners) strongSelf.ready.set(.single(true)) @@ -774,8 +774,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate } else { if case .edit(_, _, _, _, _, true, _) = self.mode { doneButtonType = .proceed - } else if case .peer = self.resultMode { - doneButtonType = .setPeer + } else if case let .peer(peer) = self.resultMode { + doneButtonType = .setPeer(peer.compactDisplayTitle, self.context.isPremium) } else { doneButtonType = .set } @@ -937,12 +937,12 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate ) } - 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)) - 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)) - 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)) - 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)) - 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)) - 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) let timestamp = self.referenceTimestamp @@ -1030,8 +1030,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate let otherPeerId = self.context.account.peerId var peers = SimpleDictionary() var messages = SimpleDictionary() - peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil) - peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil) + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) + peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) var sampleMessages: [Message] = [] @@ -1077,7 +1077,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate return state }, animated: true) }, clickThroughMessage: { - }, backgroundNode: self.backgroundNode, availableReactions: nil, isCentered: false) + }, backgroundNode: self.backgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false) return item } @@ -1179,9 +1179,12 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate } var toolbarBottomInset = layout.intrinsicInsets.bottom - if case .background = mode, toolbarBottomInset.isZero { + if case .background = self.mode, toolbarBottomInset.isZero { toolbarBottomInset = 16.0 } + if case .peer = self.resultMode, !self.state.displayPatternPanel { + toolbarBottomInset += 58.0 + } let toolbarHeight = 49.0 + toolbarBottomInset transition.updateFrame(node: self.toolbarNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - toolbarHeight), size: CGSize(width: layout.size.width, height: toolbarHeight))) self.toolbarNode.updateLayout(size: CGSize(width: layout.size.width, height: 49.0), layout: layout, transition: transition) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeColorsGridControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeColorsGridControllerNode.swift index 9566b7797c0..1b6ac7c1218 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeColorsGridControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeColorsGridControllerNode.swift @@ -157,9 +157,9 @@ final class ThemeColorsGridControllerNode: ASDisplayNode { } controller.navigationPresentation = .modal - controller.apply = { [weak self] wallpaper, _, _, _, _ in + controller.apply = { [weak self] wallpaper, _, _, _, _, forBoth in if let strongSelf = self, let mode = strongSelf.controller?.mode, case let .peer(peer) = mode, case let .wallpaper(wallpaperValue, _) = wallpaper { - let _ = (strongSelf.context.engine.themes.setChatWallpaper(peerId: peer.id, wallpaper: wallpaperValue) + let _ = (strongSelf.context.engine.themes.setChatWallpaper(peerId: peer.id, wallpaper: wallpaperValue, forBoth: forBoth) |> deliverOnMainQueue).start(completed: { dismissControllers() }) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift b/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift index 2ace0bffe48..227edafcc92 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift @@ -120,7 +120,7 @@ public final class ThemeGridController: ViewController { self.displayNode = ThemeGridControllerNode(context: self.context, presentationData: self.presentationData, presentPreviewController: { [weak self] source in if let strongSelf = self { let controller = WallpaperGalleryController(context: strongSelf.context, source: source) - controller.apply = { [weak self, weak controller] wallpaper, options, editedImage, cropRect, brightness in + controller.apply = { [weak self, weak controller] wallpaper, options, editedImage, cropRect, brightness, _ in if let strongSelf = self { uploadCustomWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, editedImage: editedImage, cropRect: cropRect, brightness: brightness, completion: { [weak self, weak controller] in if let strongSelf = self { @@ -160,7 +160,7 @@ public final class ThemeGridController: ViewController { return } let controller = WallpaperGalleryController(context: strongSelf.context, source: .asset(asset)) - controller.apply = { [weak self] wallpaper, options, editedImage, cropRect, brightness in + controller.apply = { [weak self] wallpaper, options, editedImage, cropRect, brightness, _ in if let strongSelf = self { uploadCustomWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, editedImage: editedImage, cropRect: cropRect, brightness: brightness, completion: { dismissControllers() diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index e7540c31c9c..59ebed6a4b1 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -119,7 +119,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { self.instantChatBackgroundNode.displaysAsynchronously = false self.ready.set(.single(true)) - self.instantChatBackgroundNode.update(wallpaper: wallpaper) + self.instantChatBackgroundNode.update(wallpaper: wallpaper, animated: false) self.instantChatBackgroundNode.view.contentMode = .scaleAspectFill @@ -183,7 +183,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { self.toolbarNode.cancel = { dismiss() } - self.toolbarNode.done = { [weak self] in + self.toolbarNode.done = { [weak self] _ in if let strongSelf = self { if !strongSelf.dismissed { strongSelf.dismissed = true @@ -207,7 +207,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { self.chatContainerNode.insertSubnode(self.wallpaperNode, belowSubnode: self.messagesContainerNode) } - self.wallpaperNode.update(wallpaper: self.wallpaper) + self.wallpaperNode.update(wallpaper: self.wallpaper, animated: false) self.wallpaperNode.updateBubbleTheme(bubbleTheme: self.previewTheme, bubbleCorners: self.presentationData.chatBubbleCorners) self.remoteChatBackgroundNode.imageUpdated = { [weak self] image in @@ -452,15 +452,15 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { let chatListPresentationData = ChatListPresentationData(theme: self.previewTheme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true) - 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)) - 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)) - 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)) - 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)) - 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)) - 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)) - 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)) - 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)) - 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) let timestamp = self.referenceTimestamp @@ -581,8 +581,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { let otherPeerId = self.context.account.peerId var peers = SimpleDictionary() var messages = SimpleDictionary() - peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil) - peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil) + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) + peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) var sampleMessages: [Message] = [] @@ -617,7 +617,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { sampleMessages.append(message8) items = sampleMessages.reversed().map { message in - self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message], theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperNode, availableReactions: nil, isCentered: false) + self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message], theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperNode, availableReactions: nil, accountPeer: nil, isCentered: false) } let width: CGFloat diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift index fc90663f4dd..ee60dca8a3d 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift @@ -148,7 +148,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { if currentBackgroundNode == nil { currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false) } - currentBackgroundNode?.update(wallpaper: item.wallpaper) + currentBackgroundNode?.update(wallpaper: item.wallpaper, animated: false) currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners) let insets: UIEdgeInsets @@ -163,12 +163,12 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) if let (author, text) = messageItem.reply { - peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: author, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: messageItem.nameColor, backgroundEmojiId: messageItem.backgroundEmojiId) + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: author, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: messageItem.nameColor, backgroundEmojiId: messageItem.backgroundEmojiId, profileColor: nil, profileBackgroundEmojiId: nil) messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) } - let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: messageItem.outgoing ? otherPeerId : peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: messageItem.outgoing ? TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil) : nil, text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)] : [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, isCentered: false)) + let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: messageItem.outgoing ? otherPeerId : peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: messageItem.outgoing ? TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) : nil, text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)] : [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false)) } var nodes: [ListViewItemNode] = [] diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index 764e2e6c2b2..a7740847bd3 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -122,7 +122,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { case themes(PresentationTheme, PresentationStrings, [PresentationThemeReference], PresentationThemeReference, Bool, [String: [StickerPackItem]], [Int64: PresentationThemeAccentColor], [Int64: TelegramWallpaper]) case chatTheme(PresentationTheme, String) case wallpaper(PresentationTheme, String) - case nameColor(PresentationTheme, String, String, UIColor) + case nameColor(PresentationTheme, String, String, PeerNameColors.Colors?, PeerNameColors.Colors?) case autoNight(PresentationTheme, String, Bool, Bool) case autoNightTheme(PresentationTheme, String, String) case textSize(PresentationTheme, String, String) @@ -217,8 +217,8 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { } else { return false } - case let .nameColor(lhsTheme, lhsText, lhsName, lhsColor): - if case let .nameColor(rhsTheme, rhsText, rhsName, rhsColor) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsName == rhsName, lhsColor == rhsColor { + case let .nameColor(lhsTheme, lhsText, lhsName, lhsNameColor, lhsProfileColor): + if case let .nameColor(rhsTheme, rhsText, rhsName, rhsNameColor, rhsProfileColor) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsName == rhsName, lhsNameColor == rhsNameColor, lhsProfileColor == rhsProfileColor { return true } else { return false @@ -321,8 +321,18 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: { arguments.openWallpaperSettings() }) - case let .nameColor(_, text, name, color): - return ItemListDisclosureItem(presentationData: presentationData, title: text, label: name, labelStyle: .semitransparentBadge(color), sectionId: self.section, style: .blocks, action: { + case let .nameColor(_, text, _, nameColor, profileColor): + var colors: [PeerNameColors.Colors] = [] + if let nameColor { + colors.append(nameColor) + } + if let profileColor { + colors.append(profileColor) + } + + let colorImage = generateSettingsMenuPeerColorsLabelIcon(colors: colors) + + return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", labelStyle: .image(image: colorImage, size: colorImage.size), sectionId: self.section, style: .blocks, action: { arguments.openNameColorSettings() }) case let .autoNight(_, title, value, enabled): @@ -377,14 +387,17 @@ private func themeSettingsControllerEntries(presentationData: PresentationData, entries.append(.themeListHeader(presentationData.theme, title)) let nameColor: PeerNameColor + let profileColor: PeerNameColor? var authorName = presentationData.strings.Appearance_PreviewReplyAuthor if let accountPeer { nameColor = accountPeer.nameColor ?? .blue if accountPeer._asPeer().hasCustomNameColor { authorName = accountPeer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder) } + profileColor = accountPeer.profileColor } else { nameColor = .blue + profileColor = nil } entries.append(.chatPreview(presentationData.theme, presentationData.chatWallpaper, presentationData.chatFontSize, presentationData.chatBubbleCorners, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, [ChatPreviewMessageItem(outgoing: false, reply: (authorName, presentationData.strings.Appearance_PreviewReplyText), text: presentationData.strings.Appearance_PreviewIncomingText, nameColor: nameColor, backgroundEmojiId: accountPeer?.backgroundEmojiId), ChatPreviewMessageItem(outgoing: true, reply: nil, text: presentationData.strings.Appearance_PreviewOutgoingText, nameColor: .blue, backgroundEmojiId: nil)])) @@ -393,8 +406,9 @@ private func themeSettingsControllerEntries(presentationData: PresentationData, entries.append(.chatTheme(presentationData.theme, strings.Settings_ChatThemes)) entries.append(.wallpaper(presentationData.theme, strings.Settings_ChatBackground)) - let colors = nameColors.get(nameColor) - entries.append(.nameColor(presentationData.theme, strings.Appearance_NameColor, accountPeer?.compactDisplayTitle ?? "", colors.main)) + let colors = nameColors.get(nameColor, dark: presentationData.theme.overallDarkAppearance) + let profileColors = profileColor.flatMap { nameColors.getProfile($0, dark: presentationData.theme.overallDarkAppearance, subject: .palette) } + entries.append(.nameColor(presentationData.theme, presentationData.strings.Settings_YourColor, accountPeer?.compactDisplayTitle ?? "", colors, profileColors)) entries.append(.autoNight(presentationData.theme, strings.Appearance_NightTheme, presentationThemeSettings.automaticThemeSwitchSetting.force, !presentationData.autoNightModeTriggered || presentationThemeSettings.automaticThemeSwitchSetting.force)) let autoNightMode: String diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift index e09e5fbbcf0..18c55a2f023 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift @@ -192,7 +192,7 @@ public class WallpaperGalleryController: ViewController { private let context: AccountContext private let source: WallpaperListSource private let mode: Mode - public var apply: ((WallpaperGalleryEntry, WallpaperPresentationOptions, UIImage?, CGRect?, CGFloat?) -> Void)? + public var apply: ((WallpaperGalleryEntry, WallpaperPresentationOptions, UIImage?, CGRect?, CGFloat?, Bool) -> Void)? private var interaction: WallpaperGalleryInteraction? @@ -497,8 +497,8 @@ public class WallpaperGalleryController: ViewController { default: break } - if case .peer = self.mode { - doneButtonType = .setPeer + if case let .peer(peer, _) = self.mode { + doneButtonType = .setPeer(peer.compactDisplayTitle, self.context.isPremium) } let toolbarNode = WallpaperGalleryToolbarNode(theme: presentationData.theme, strings: presentationData.strings, doneButtonType: doneButtonType) @@ -515,8 +515,23 @@ public class WallpaperGalleryController: ViewController { self?.dismiss(forceAway: true) } var dismissed = false - toolbarNode.done = { [weak self] in + toolbarNode.done = { [weak self] forBoth in if let strongSelf = self, !dismissed { + if forBoth && !strongSelf.context.isPremium { + let context = strongSelf.context + var replaceImpl: ((ViewController) -> Void)? + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .wallpapers, action: { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .wallpapers, forceDark: false, dismissed: nil) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + strongSelf.push(controller) + + return + } + if let centralItemNode = strongSelf.galleryNode.pager.centralItemNode() as? WallpaperGalleryItemNode { if centralItemNode.cropNode.scrollNode.view.isDecelerating { return @@ -554,12 +569,12 @@ public class WallpaperGalleryController: ViewController { |> filter({ $0.complete }) |> take(1) |> deliverOnMainQueue).start(next: { _ in - apply?(entry, options, nil, nil, centralItemNode.brightness) + apply?(entry, options, nil, nil, centralItemNode.brightness, forBoth) }) } } } else { - apply?(entry, options, centralItemNode.editedFullSizeImage, centralItemNode.editedCropRect, centralItemNode.brightness) + apply?(entry, options, centralItemNode.editedFullSizeImage, centralItemNode.editedCropRect, centralItemNode.brightness, forBoth) } return } @@ -717,7 +732,7 @@ public class WallpaperGalleryController: ViewController { break } - strongSelf.apply?(entry, options, nil, centralItemNode.cropRect, centralItemNode.brightness) + strongSelf.apply?(entry, options, nil, centralItemNode.cropRect, centralItemNode.brightness, forBoth) } } } @@ -953,7 +968,10 @@ public class WallpaperGalleryController: ViewController { self.galleryNode.containerLayoutUpdated(pagerLayout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition) self.overlayNode?.frame = self.galleryNode.bounds - let toolbarHeight: CGFloat = 66.0 + var toolbarHeight: CGFloat = 66.0 + if case .peer = self.mode { + toolbarHeight += 58.0 + } transition.updateFrame(node: self.toolbarNode!, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - toolbarHeight - layout.intrinsicInsets.bottom), size: CGSize(width: layout.size.width, height: toolbarHeight + layout.intrinsicInsets.bottom))) self.toolbarNode!.updateLayout(size: CGSize(width: layout.size.width, height: toolbarHeight), layout: layout, transition: transition) @@ -1051,7 +1069,7 @@ public class WallpaperGalleryController: ViewController { self.toolbarNode?.setDoneIsSolid(self.patternPanelEnabled || self.colorsPanelEnabled, transition: transition) - bottomInset += 66.0 + bottomInset += toolbarHeight self.validLayout = (layout, bottomInset) if !hadLayout { diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index 6b721d5afb1..01fb70c683e 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -636,7 +636,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { switch entry { case let .wallpaper(wallpaper, _): Queue.mainQueue().justDispatch { - self.nativeNode.update(wallpaper: wallpaper) + self.nativeNode.update(wallpaper: wallpaper, animated: false) } if case let .file(file) = wallpaper, file.isPattern { @@ -651,7 +651,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { isColor = true } else if case let .gradient(gradient) = wallpaper { self.nativeNode.isHidden = false - self.nativeNode.update(wallpaper: wallpaper) + self.nativeNode.update(wallpaper: wallpaper, animated: false) self.patternButtonNode.isSelected = false if gradient.colors.count >= 3 { @@ -662,7 +662,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { isColor = true } else if case .color = wallpaper { self.nativeNode.isHidden = false - self.nativeNode.update(wallpaper: wallpaper) + self.nativeNode.update(wallpaper: wallpaper, animated: false) self.patternButtonNode.isSelected = false isColor = true } else { @@ -989,7 +989,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { strongSelf.context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) let wallpaper: TelegramWallpaper = .image([TelegramMediaImageRepresentation(dimensions: PixelDimensions(image.size), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)], WallpaperSettings()) - strongSelf.nativeNode.update(wallpaper: wallpaper) + strongSelf.nativeNode.update(wallpaper: wallpaper, animated: false) } } } @@ -1329,7 +1329,10 @@ final class WallpaperGalleryItemNode: GalleryItemNode { let buttonSpacing: CGFloat = 18.0 - let toolbarHeight: CGFloat = 66.0 + var toolbarHeight: CGFloat = 66.0 + if let mode = self.mode, case .peer = mode { + toolbarHeight += 58.0 + } let leftButtonFrame = CGRect(origin: CGPoint(x: floor(layout.size.width / 2.0 - buttonSize.width - buttonSpacing) + offset.x, y: layout.size.height - toolbarHeight - layout.intrinsicInsets.bottom - 54.0 + offset.y + additionalYOffset), size: buttonSize) let centerButtonFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - buttonSize.width) / 2.0) + offset.x, y: layout.size.height - toolbarHeight - layout.intrinsicInsets.bottom - 54.0 + offset.y + additionalYOffset), size: buttonSize) @@ -1479,14 +1482,17 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.nativeNode.updateBubbleTheme(bubbleTheme: self.presentationData.theme, bubbleCorners: self.presentationData.chatBubbleCorners) var bottomInset: CGFloat = 132.0 + if let mode = self.mode, case .peer = mode { + bottomInset += 58.0 + } var items: [ListViewItem] = [] let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)) let otherPeerId = self.context.account.peerId var peers = SimpleDictionary() let messages = SimpleDictionary() - peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_PreviewReplyAuthor, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil) - peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_PreviewReplyAuthor, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil) + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_PreviewReplyAuthor, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) + peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_PreviewReplyAuthor, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) var topMessageText = "" var bottomMessageText = "" @@ -1574,17 +1580,17 @@ final class WallpaperGalleryItemNode: GalleryItemNode { let theme = self.presentationData.theme let message1 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: bottomMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, isCentered: false)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, accountPeer: nil, isCentered: false)) let message2 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: topMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, isCentered: false)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, accountPeer: nil, isCentered: false)) if let serviceMessageText { let attributedText = convertMarkdownToAttributes(NSAttributedString(string: serviceMessageText)) let entities = generateChatInputTextEntities(attributedText) let message3 = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: attributedText.string, entities: entities))], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, isCentered: false)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, accountPeer: nil, isCentered: false)) } let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height) diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryToolbarNode.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryToolbarNode.swift index 63d5867a594..0a0f2f41422 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryToolbarNode.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryToolbarNode.swift @@ -3,6 +3,7 @@ import UIKit import AsyncDisplayKit import Display import TelegramPresentationData +import ManagedAnimationNode enum WallpaperGalleryToolbarCancelButtonType { case cancel @@ -11,7 +12,7 @@ enum WallpaperGalleryToolbarCancelButtonType { enum WallpaperGalleryToolbarDoneButtonType { case set - case setPeer + case setPeer(String, Bool) case proceed case apply case none @@ -22,7 +23,7 @@ protocol WallpaperGalleryToolbar: ASDisplayNode { var doneButtonType: WallpaperGalleryToolbarDoneButtonType { get set } var cancel: (() -> Void)? { get set } - var done: (() -> Void)? { get set } + var done: ((Bool) -> Void)? { get set } func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) @@ -30,6 +31,170 @@ protocol WallpaperGalleryToolbar: ASDisplayNode { } final class WallpaperGalleryToolbarNode: ASDisplayNode, WallpaperGalleryToolbar { + class ButtonNode: ASDisplayNode { + private let doneButton = HighlightTrackingButtonNode() + private var doneButtonBackgroundNode: ASDisplayNode + private let doneButtonTitleNode: ImmediateTextNode + private let doneButtonSolidBackgroundNode: ASDisplayNode + private let doneButtonSolidTitleNode: ImmediateTextNode + + private let animationNode: SimpleAnimationNode + + var action: () -> Void = {} + + var isLocked: Bool = false { + didSet { + self.animationNode.isHidden = !self.isLocked + } + } + + override init() { + self.doneButtonBackgroundNode = WallpaperLightButtonBackgroundNode() + self.doneButtonBackgroundNode.cornerRadius = 14.0 + + self.doneButtonTitleNode = ImmediateTextNode() + self.doneButtonTitleNode.displaysAsynchronously = false + self.doneButtonTitleNode.isUserInteractionEnabled = false + + self.doneButtonSolidBackgroundNode = ASDisplayNode() + self.doneButtonSolidBackgroundNode.alpha = 0.0 + self.doneButtonSolidBackgroundNode.clipsToBounds = true + self.doneButtonSolidBackgroundNode.layer.cornerRadius = 14.0 + if #available(iOS 13.0, *) { + self.doneButtonSolidBackgroundNode.layer.cornerCurve = .continuous + } + self.doneButtonSolidBackgroundNode.isUserInteractionEnabled = false + + self.doneButtonSolidTitleNode = ImmediateTextNode() + self.doneButtonSolidTitleNode.alpha = 0.0 + self.doneButtonSolidTitleNode.displaysAsynchronously = false + self.doneButtonSolidTitleNode.isUserInteractionEnabled = false + + self.animationNode = SimpleAnimationNode(animationName: "premium_unlock", size: CGSize(width: 30.0, height: 30.0)) + self.animationNode.customColor = .white + self.animationNode.isHidden = true + + super.init() + + self.doneButton.isExclusiveTouch = true + + self.addSubnode(self.doneButtonBackgroundNode) + self.addSubnode(self.doneButtonTitleNode) + + self.addSubnode(self.doneButtonSolidBackgroundNode) + self.addSubnode(self.doneButtonSolidTitleNode) + + self.addSubnode(self.animationNode) + + self.addSubnode(self.doneButton) + + self.doneButton.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + if strongSelf.isSolid { + strongSelf.doneButtonSolidBackgroundNode.layer.removeAnimation(forKey: "opacity") + strongSelf.doneButtonSolidBackgroundNode.alpha = 0.55 + strongSelf.doneButtonSolidTitleNode.layer.removeAnimation(forKey: "opacity") + strongSelf.doneButtonSolidTitleNode.alpha = 0.55 + } else { + strongSelf.doneButtonBackgroundNode.layer.removeAnimation(forKey: "opacity") + strongSelf.doneButtonBackgroundNode.alpha = 0.55 + strongSelf.doneButtonTitleNode.layer.removeAnimation(forKey: "opacity") + strongSelf.doneButtonTitleNode.alpha = 0.55 + } + } else { + if strongSelf.isSolid { + strongSelf.doneButtonSolidBackgroundNode.alpha = 1.0 + strongSelf.doneButtonSolidBackgroundNode.layer.animateAlpha(from: 0.55, to: 1.0, duration: 0.2) + strongSelf.doneButtonSolidTitleNode.alpha = 1.0 + strongSelf.doneButtonSolidTitleNode.layer.animateAlpha(from: 0.55, to: 1.0, duration: 0.2) + } else { + strongSelf.doneButtonBackgroundNode.alpha = 1.0 + strongSelf.doneButtonBackgroundNode.layer.animateAlpha(from: 0.55, to: 1.0, duration: 0.2) + strongSelf.doneButtonTitleNode.alpha = 1.0 + strongSelf.doneButtonTitleNode.layer.animateAlpha(from: 0.55, to: 1.0, duration: 0.2) + } + } + } + } + + self.doneButton.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside) + } + + func setEnabled(_ enabled: Bool) { + self.doneButton.alpha = enabled ? 1.0 : 0.4 + self.doneButton.isUserInteractionEnabled = enabled + } + + private var isSolid = false + func setIsSolid(_ isSolid: Bool, transition: ContainedViewLayoutTransition) { + guard self.isSolid != isSolid else { + return + } + self.isSolid = isSolid + + transition.updateAlpha(node: self.doneButtonBackgroundNode, alpha: isSolid ? 0.0 : 1.0) + transition.updateAlpha(node: self.doneButtonSolidBackgroundNode, alpha: isSolid ? 1.0 : 0.0) + transition.updateAlpha(node: self.doneButtonTitleNode, alpha: isSolid ? 0.0 : 1.0) + transition.updateAlpha(node: self.doneButtonSolidTitleNode, alpha: isSolid ? 1.0 : 0.0) + } + + func updateTitle(_ title: String, theme: PresentationTheme) { + self.doneButtonTitleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(17.0), textColor: .white) + + self.doneButtonSolidBackgroundNode.backgroundColor = theme.list.itemCheckColors.fillColor + self.doneButtonSolidTitleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor) + } + + func updateSize(_ size: CGSize) { + let bounds = CGRect(origin: .zero, size: size) + self.doneButtonBackgroundNode.frame = bounds + if let backgroundNode = self.doneButtonBackgroundNode as? WallpaperOptionBackgroundNode { + backgroundNode.updateLayout(size: size) + } else if let backgroundNode = self.doneButtonBackgroundNode as? WallpaperLightButtonBackgroundNode { + backgroundNode.updateLayout(size: size) + } + self.doneButtonSolidBackgroundNode.frame = bounds + + let constrainedSize = CGSize(width: size.width - 44.0, height: size.height) + let iconSize = CGSize(width: 30.0, height: 30.0) + let doneTitleSize = self.doneButtonTitleNode.updateLayout(constrainedSize) + + var totalWidth = doneTitleSize.width + if self.isLocked { + totalWidth += iconSize.width + 1.0 + } + let titleOriginX = floorToScreenPixels((bounds.width - totalWidth) / 2.0) + + self.animationNode.frame = CGRect(origin: CGPoint(x: titleOriginX, y: floorToScreenPixels((bounds.height - iconSize.height) / 2.0)), size: iconSize) + self.doneButtonTitleNode.frame = CGRect(origin: CGPoint(x: titleOriginX + totalWidth - doneTitleSize.width, y: floorToScreenPixels((bounds.height - doneTitleSize.height) / 2.0)), size: doneTitleSize).offsetBy(dx: bounds.minX, dy: bounds.minY) + + let _ = self.doneButtonSolidTitleNode.updateLayout(constrainedSize) + self.doneButtonSolidTitleNode.frame = self.doneButtonTitleNode.frame + + self.doneButton.frame = bounds + } + + var dark: Bool = false { + didSet { + if self.dark != oldValue { + self.doneButtonBackgroundNode.removeFromSupernode() + if self.dark { + self.doneButtonBackgroundNode = WallpaperOptionBackgroundNode(enableSaturation: true) + } else { + self.doneButtonBackgroundNode = WallpaperLightButtonBackgroundNode() + } + self.doneButtonBackgroundNode.cornerRadius = 14.0 + self.insertSubnode(self.doneButtonBackgroundNode, at: 0) + } + } + } + + @objc func pressed() { + self.action() + } + } + private var theme: PresentationTheme private let strings: PresentationStrings @@ -44,110 +209,49 @@ final class WallpaperGalleryToolbarNode: ASDisplayNode, WallpaperGalleryToolbar } } - var dark: Bool { + var dark: Bool = false { didSet { - if self.dark != oldValue { - self.doneButtonBackgroundNode.removeFromSupernode() - if self.dark { - self.doneButtonBackgroundNode = WallpaperOptionBackgroundNode(enableSaturation: true) - } else { - self.doneButtonBackgroundNode = WallpaperLightButtonBackgroundNode() - } - self.doneButtonBackgroundNode.cornerRadius = 14.0 - self.insertSubnode(self.doneButtonBackgroundNode, at: 0) - } + self.applyButton.dark = self.dark + self.applyForBothButton.dark = self.dark } } - private let doneButton = HighlightTrackingButtonNode() - private var doneButtonBackgroundNode: ASDisplayNode - - private let doneButtonTitleNode: ImmediateTextNode - - private let doneButtonSolidBackgroundNode: ASDisplayNode - private let doneButtonSolidTitleNode: ImmediateTextNode + private let applyButton = ButtonNode() + private let applyForBothButton = ButtonNode() var cancel: (() -> Void)? - var done: (() -> Void)? + var done: ((Bool) -> Void)? init(theme: PresentationTheme, strings: PresentationStrings, cancelButtonType: WallpaperGalleryToolbarCancelButtonType = .cancel, doneButtonType: WallpaperGalleryToolbarDoneButtonType = .set) { self.theme = theme self.strings = strings self.cancelButtonType = cancelButtonType self.doneButtonType = doneButtonType - self.dark = false - - self.doneButtonBackgroundNode = WallpaperLightButtonBackgroundNode() - self.doneButtonBackgroundNode.cornerRadius = 14.0 - self.doneButtonTitleNode = ImmediateTextNode() - self.doneButtonTitleNode.displaysAsynchronously = false - self.doneButtonTitleNode.isUserInteractionEnabled = false - - self.doneButtonSolidBackgroundNode = ASDisplayNode() - self.doneButtonSolidBackgroundNode.alpha = 0.0 - self.doneButtonSolidBackgroundNode.clipsToBounds = true - self.doneButtonSolidBackgroundNode.layer.cornerRadius = 14.0 - if #available(iOS 13.0, *) { - self.doneButtonSolidBackgroundNode.layer.cornerCurve = .continuous - } - self.doneButtonSolidBackgroundNode.isUserInteractionEnabled = false - - self.doneButtonSolidTitleNode = ImmediateTextNode() - self.doneButtonSolidTitleNode.alpha = 0.0 - self.doneButtonSolidTitleNode.displaysAsynchronously = false - self.doneButtonSolidTitleNode.isUserInteractionEnabled = false - super.init() - self.doneButton.isExclusiveTouch = true - - self.addSubnode(self.doneButtonBackgroundNode) - self.addSubnode(self.doneButtonTitleNode) - - self.addSubnode(self.doneButtonSolidBackgroundNode) - self.addSubnode(self.doneButtonSolidTitleNode) - - self.addSubnode(self.doneButton) + self.addSubnode(self.applyButton) + if case .setPeer = doneButtonType { + self.addSubnode(self.applyForBothButton) + } self.updateThemeAndStrings(theme: theme, strings: strings) - self.doneButton.highligthedChanged = { [weak self] highlighted in - if let strongSelf = self { - if highlighted { - if strongSelf.isSolid { - strongSelf.doneButtonSolidBackgroundNode.layer.removeAnimation(forKey: "opacity") - strongSelf.doneButtonSolidBackgroundNode.alpha = 0.55 - strongSelf.doneButtonSolidTitleNode.layer.removeAnimation(forKey: "opacity") - strongSelf.doneButtonSolidTitleNode.alpha = 0.55 - } else { - strongSelf.doneButtonBackgroundNode.layer.removeAnimation(forKey: "opacity") - strongSelf.doneButtonBackgroundNode.alpha = 0.55 - strongSelf.doneButtonTitleNode.layer.removeAnimation(forKey: "opacity") - strongSelf.doneButtonTitleNode.alpha = 0.55 - } - } else { - if strongSelf.isSolid { - strongSelf.doneButtonSolidBackgroundNode.alpha = 1.0 - strongSelf.doneButtonSolidBackgroundNode.layer.animateAlpha(from: 0.55, to: 1.0, duration: 0.2) - strongSelf.doneButtonSolidTitleNode.alpha = 1.0 - strongSelf.doneButtonSolidTitleNode.layer.animateAlpha(from: 0.55, to: 1.0, duration: 0.2) - } else { - strongSelf.doneButtonBackgroundNode.alpha = 1.0 - strongSelf.doneButtonBackgroundNode.layer.animateAlpha(from: 0.55, to: 1.0, duration: 0.2) - strongSelf.doneButtonTitleNode.alpha = 1.0 - strongSelf.doneButtonTitleNode.layer.animateAlpha(from: 0.55, to: 1.0, duration: 0.2) - } - } + self.applyButton.action = { [weak self] in + if let self { + self.done?(false) + } + } + self.applyForBothButton.action = { [weak self] in + if let self { + self.done?(true) } } - - self.doneButton.addTarget(self, action: #selector(self.donePressed), forControlEvents: .touchUpInside) } func setDoneEnabled(_ enabled: Bool) { - self.doneButton.alpha = enabled ? 1.0 : 0.4 - self.doneButton.isUserInteractionEnabled = enabled + self.applyButton.setEnabled(enabled) + self.applyForBothButton.setEnabled(enabled) } private var isSolid = false @@ -155,65 +259,65 @@ final class WallpaperGalleryToolbarNode: ASDisplayNode, WallpaperGalleryToolbar guard self.isSolid != isSolid else { return } - self.isSolid = isSolid - transition.updateAlpha(node: self.doneButtonBackgroundNode, alpha: isSolid ? 0.0 : 1.0) - transition.updateAlpha(node: self.doneButtonSolidBackgroundNode, alpha: isSolid ? 1.0 : 0.0) - transition.updateAlpha(node: self.doneButtonTitleNode, alpha: isSolid ? 0.0 : 1.0) - transition.updateAlpha(node: self.doneButtonSolidTitleNode, alpha: isSolid ? 1.0 : 0.0) + self.isSolid = isSolid + self.applyButton.setIsSolid(isSolid, transition: transition) + self.applyForBothButton.setIsSolid(isSolid, transition: transition) } func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { self.theme = theme - let doneTitle: String + let applyTitle: String + var applyForBothTitle: String? = nil + var applyForBothLocked = false switch self.doneButtonType { - case .set: - doneTitle = strings.Wallpaper_ApplyForAll - case .setPeer: - doneTitle = strings.Wallpaper_ApplyForChat - case .proceed: - doneTitle = strings.Theme_Colors_Proceed - case .apply: - doneTitle = strings.WallpaperPreview_PatternPaternApply - case .none: - doneTitle = "" - self.doneButton.isUserInteractionEnabled = false + case .set: + applyTitle = strings.Wallpaper_ApplyForAll + case let .setPeer(name, isPremium): + applyTitle = strings.Wallpaper_ApplyForMe + applyForBothTitle = strings.Wallpaper_ApplyForBoth(name).string + applyForBothLocked = !isPremium + case .proceed: + applyTitle = strings.Theme_Colors_Proceed + case .apply: + applyTitle = strings.WallpaperPreview_PatternPaternApply + case .none: + applyTitle = "" + self.applyButton.isUserInteractionEnabled = false } - self.doneButtonTitleNode.attributedText = NSAttributedString(string: doneTitle, font: Font.semibold(17.0), textColor: .white) - self.doneButtonSolidBackgroundNode.backgroundColor = theme.list.itemCheckColors.fillColor - self.doneButtonSolidTitleNode.attributedText = NSAttributedString(string: doneTitle, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor) + self.applyButton.updateTitle(applyTitle, theme: theme) + if let applyForBothTitle { + self.applyForBothButton.updateTitle(applyForBothTitle, theme: theme) + } + self.applyForBothButton.isLocked = applyForBothLocked } func updateLayout(size: CGSize, layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { let inset: CGFloat = 16.0 let buttonHeight: CGFloat = 50.0 - let doneFrame = CGRect(origin: CGPoint(x: inset, y: 2.0), size: CGSize(width: size.width - inset * 2.0, height: buttonHeight)) - self.doneButton.frame = doneFrame - self.doneButtonBackgroundNode.frame = doneFrame - if let backgroundNode = self.doneButtonBackgroundNode as? WallpaperOptionBackgroundNode { - backgroundNode.updateLayout(size: doneFrame.size) - } else if let backgroundNode = self.doneButtonBackgroundNode as? WallpaperLightButtonBackgroundNode { - backgroundNode.updateLayout(size: doneFrame.size) - } - self.doneButtonSolidBackgroundNode.frame = doneFrame + let spacing: CGFloat = 8.0 - let doneTitleSize = self.doneButtonTitleNode.updateLayout(doneFrame.size) - self.doneButtonTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((doneFrame.width - doneTitleSize.width) / 2.0), y: floorToScreenPixels((doneFrame.height - doneTitleSize.height) / 2.0)), size: doneTitleSize).offsetBy(dx: doneFrame.minX, dy: doneFrame.minY) + let applyFrame = CGRect(origin: CGPoint(x: inset, y: 2.0), size: CGSize(width: size.width - inset * 2.0, height: buttonHeight)) + let applyForBothFrame = CGRect(origin: CGPoint(x: inset, y: applyFrame.maxY + spacing), size: CGSize(width: size.width - inset * 2.0, height: buttonHeight)) - let _ = self.doneButtonSolidTitleNode.updateLayout(doneFrame.size) - self.doneButtonSolidTitleNode.frame = self.doneButtonTitleNode.frame + var showApplyForBothButton = false + if case .setPeer = self.doneButtonType { + showApplyForBothButton = true + } + transition.updateAlpha(node: self.applyForBothButton, alpha: showApplyForBothButton ? 1.0 : 0.0) + + self.applyButton.frame = applyFrame + self.applyButton.updateSize(applyFrame.size) + self.applyForBothButton.frame = applyForBothFrame + self.applyForBothButton.updateSize(applyForBothFrame.size) } @objc func cancelPressed() { self.cancel?() } - - @objc func donePressed() { - self.done?() - } } final class WallpaperGalleryOldToolbarNode: ASDisplayNode, WallpaperGalleryToolbar { @@ -240,7 +344,7 @@ final class WallpaperGalleryOldToolbarNode: ASDisplayNode, WallpaperGalleryToolb private let topSeparatorNode = ASDisplayNode() var cancel: (() -> Void)? - var done: (() -> Void)? + var done: ((Bool) -> Void)? init(theme: PresentationTheme, strings: PresentationStrings, cancelButtonType: WallpaperGalleryToolbarCancelButtonType = .cancel, doneButtonType: WallpaperGalleryToolbarDoneButtonType = .set) { self.theme = theme @@ -343,6 +447,6 @@ final class WallpaperGalleryOldToolbarNode: ASDisplayNode, WallpaperGalleryToolb } @objc func donePressed() { - self.done?() + self.done?(false) } } diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index d42564a6d0e..c5d5d31f88d 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -22,6 +22,9 @@ import WallpaperBackgroundNode import TelegramIntents import AnimationCache import MultiAnimationRenderer +import ObjectiveC + +private var ObjCKey_DeinitWatcher: Int? public struct ShareControllerAction { let title: String @@ -468,6 +471,8 @@ public final class ShareController: ViewController { } public var openShareAsImage: (([Message]) -> Void)? + + public var shareStory: (() -> Void)? public var debugAction: (() -> Void)? @@ -725,7 +730,7 @@ public final class ShareController: ViewController { return } strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: title, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - }, externalShare: self.externalShare, immediateExternalShare: self.immediateExternalShare, immediatePeerId: self.immediatePeerId, fromForeignApp: self.fromForeignApp, forceTheme: self.forceTheme, fromPublicChannel: fromPublicChannel, segmentedValues: self.segmentedValues) + }, externalShare: self.externalShare, immediateExternalShare: self.immediateExternalShare, immediatePeerId: self.immediatePeerId, fromForeignApp: self.fromForeignApp, forceTheme: self.forceTheme, fromPublicChannel: fromPublicChannel, segmentedValues: self.segmentedValues, shareStory: self.shareStory) self.controllerNode.completed = self.completed self.controllerNode.present = { [weak self] c in self?.presentInGlobalOverlay(c) @@ -1020,7 +1025,6 @@ public final class ShareController: ViewController { subject = selectedValue.subject } var messageUrl: String? -// var messagesToShare: [Message]? switch subject { case let .url(text): collectableItems.append(CollectableExternalShareItem(url: explicitUrl(text), text: "", author: nil, timestamp: nil, mediaReference: nil)) @@ -1037,7 +1041,6 @@ public final class ShareController: ViewController { let latLong = "\(media.latitude),\(media.longitude)" collectableItems.append(CollectableExternalShareItem(url: "https://maps.apple.com/maps?ll=\(latLong)&q=\(latLong)&t=m", text: "", author: nil, timestamp: nil, mediaReference: nil)) case let .messages(messages): -// messagesToShare = messages for message in messages { var url: String? var selectedMedia: Media? @@ -1131,16 +1134,52 @@ public final class ShareController: ViewController { |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in -// if asImage, let messages = messagesToShare { -// self?.openShareAsImage?(messages) -// } else { - let activityController = UIActivityViewController(activityItems: activityItems, applicationActivities: activities) - if let strongSelf = self, let window = strongSelf.view.window, let rootViewController = window.rootViewController { - activityController.popoverPresentationController?.sourceView = window - activityController.popoverPresentationController?.sourceRect = CGRect(origin: CGPoint(x: window.bounds.width / 2.0, y: window.bounds.size.height - 1.0), size: CGSize(width: 1.0, height: 1.0)) - rootViewController.present(activityController, animated: true, completion: nil) + let activityController = UIActivityViewController(activityItems: activityItems, applicationActivities: activities) + if let strongSelf = self, let window = strongSelf.view.window, let rootViewController = window.rootViewController { + activityController.popoverPresentationController?.sourceView = window + activityController.popoverPresentationController?.sourceRect = CGRect(origin: CGPoint(x: window.bounds.width / 2.0, y: window.bounds.size.height - 1.0), size: CGSize(width: 1.0, height: 1.0)) + rootViewController.present(activityController, animated: true, completion: nil) + + final class DeinitWatcher: NSObject { + let f: () -> Void + + init(_ f: @escaping () -> Void) { + self.f = f + } + + deinit { + f() + } + } + + let watchDisposable = MetaDisposable() + objc_setAssociatedObject(activityController, &ObjCKey_DeinitWatcher, DeinitWatcher { + watchDisposable.dispose() + }, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + + if case let .messages(messages) = subject { + watchDisposable.set((currentContext.context.engine.data.subscribe( + EngineDataMap(messages.map { TelegramEngine.EngineData.Item.Messages.Message(id: $0.id) }) + ) + |> deliverOnMainQueue).start(next: { [weak activityController] currentMessages in + guard let activityController else { + return + } + var allFound = true + for message in messages { + if let value = currentMessages[message.id], value != nil { + } else { + allFound = false + break + } + } + + if !allFound { + activityController.presentingViewController?.dismiss(animated: true) + } + })) } -// } + } }) } return .done @@ -2466,134 +2505,6 @@ public final class ShareController: ViewController { } } - -final class MessageStoryRenderer { - private let context: AccountContext - private let presentationData: PresentationData - private let messages: [Message] - - let containerNode: ASDisplayNode - private let instantChatBackgroundNode: WallpaperBackgroundNode - private let messagesContainerNode: ASDisplayNode - private var dateHeaderNode: ListViewItemHeaderNode? - private var messageNodes: [ListViewItemNode]? - private let addressNode: ImmediateTextNode - - init(context: AccountContext, messages: [Message]) { - self.context = context - self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - self.messages = messages - - self.containerNode = ASDisplayNode() - - self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false) - self.instantChatBackgroundNode.displaysAsynchronously = false - - self.messagesContainerNode = ASDisplayNode() - self.messagesContainerNode.clipsToBounds = true - self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) - - let message = messages.first! - let addressName = message.peers[message.id.peerId]?.addressName ?? "" - - self.addressNode = ImmediateTextNode() - self.addressNode.displaysAsynchronously = false - self.addressNode.attributedText = NSAttributedString(string: "t.me/\(addressName)/\(message.id.id)", font: Font.medium(14.0), textColor: UIColor(rgb: 0xffffff)) - self.addressNode.textShadowColor = UIColor(rgb: 0x929292, alpha: 0.8) - - self.containerNode.addSubnode(self.instantChatBackgroundNode) - self.containerNode.addSubnode(self.messagesContainerNode) - self.containerNode.addSubnode(self.addressNode) - } - - func update(layout: ContainerViewLayout, completion: @escaping (UIImage?) -> Void) { - self.updateMessagesLayout(layout: layout) - - Queue.mainQueue().after(0.01) { - UIGraphicsBeginImageContextWithOptions(layout.size, false, 3.0) - self.containerNode.view.drawHierarchy(in: CGRect(origin: CGPoint(), size: layout.size), afterScreenUpdates: true) - let img = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - completion(img) - } - } - - private func updateMessagesLayout(layout: ContainerViewLayout) { - let size = layout.size - self.containerNode.frame = CGRect(origin: CGPoint(), size: layout.size) - self.instantChatBackgroundNode.frame = CGRect(origin: CGPoint(), size: layout.size) - self.instantChatBackgroundNode.updateLayout(size: size, displayMode: .aspectFill, transition: .immediate) - self.messagesContainerNode.frame = CGRect(origin: CGPoint(), size: layout.size) - - let addressLayout = self.addressNode.updateLayout(size) - - let theme = self.presentationData.theme.withUpdated(preview: true) - let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.messages.first?.timestamp ?? 0, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder) - - let items: [ListViewItem] = [self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: self.messages, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.theme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: nil, availableReactions: nil, isCentered: false)] - - let inset: CGFloat = 16.0 - let width = layout.size.width - inset * 2.0 - let params = ListViewItemLayoutParams(width: width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height) - if let messageNodes = self.messageNodes { - for i in 0 ..< items.count { - let itemNode = messageNodes[i] - items[i].updateNode(async: { $0() }, node: { - return itemNode - }, params: params, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], animation: .None, completion: { (layout, apply) in - let nodeFrame = CGRect(origin: CGPoint(x: 0.0, y: floor((size.height - layout.size.height) / 2.0)), size: CGSize(width: width, height: layout.size.height)) - - itemNode.contentSize = layout.contentSize - itemNode.insets = layout.insets - itemNode.frame = nodeFrame - itemNode.isUserInteractionEnabled = false - - apply(ListViewItemApply(isOnScreen: true)) - }) - } - } else { - var messageNodes: [ListViewItemNode] = [] - for i in 0 ..< items.count { - var itemNode: ListViewItemNode? - items[i].nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: true, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], completion: { node, apply in - itemNode = node - apply().1(ListViewItemApply(isOnScreen: true)) - }) - itemNode!.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0) - itemNode!.isUserInteractionEnabled = false - messageNodes.append(itemNode!) - self.messagesContainerNode.addSubnode(itemNode!) - } - self.messageNodes = messageNodes - } - - var bottomOffset: CGFloat = 0.0 - if let messageNodes = self.messageNodes { - for itemNode in messageNodes { - itemNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((size.height - itemNode.frame.height) / 2.0)), size: itemNode.frame.size) - bottomOffset += itemNode.frame.maxY - itemNode.updateFrame(itemNode.frame, within: layout.size) - } - } - - self.addressNode.frame = CGRect(origin: CGPoint(x: inset + 16.0, y: bottomOffset + 3.0), size: CGSize(width: addressLayout.width, height: addressLayout.height + 3.0)) - - let dateHeaderNode: ListViewItemHeaderNode - if let currentDateHeaderNode = self.dateHeaderNode { - dateHeaderNode = currentDateHeaderNode - headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem) - } else { - dateHeaderNode = headerItem.node(synchronousLoad: true) - dateHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0) - self.messagesContainerNode.addSubnode(dateHeaderNode) - self.dateHeaderNode = dateHeaderNode - } - - dateHeaderNode.frame = CGRect(origin: CGPoint(x: 0.0, y: bottomOffset), size: CGSize(width: layout.size.width, height: headerItem.height)) - dateHeaderNode.updateLayout(size: self.containerNode.frame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right) - } -} - public class ShareToInstagramActivity: UIActivity { private let context: AccountContext private var activityItems = [Any]() diff --git a/submodules/ShareController/Sources/ShareControllerNode.swift b/submodules/ShareController/Sources/ShareControllerNode.swift index 88aee95e8e5..647108975ea 100644 --- a/submodules/ShareController/Sources/ShareControllerNode.swift +++ b/submodules/ShareController/Sources/ShareControllerNode.swift @@ -87,7 +87,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate private let showNames = ValuePromise(true) - init(environment: ShareControllerEnvironment, presentationData: PresentationData, presetText: String?, defaultAction: ShareControllerAction?, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, presentError: @escaping (String?, String) -> Void, externalShare: Bool, immediateExternalShare: Bool, immediatePeerId: PeerId?, fromForeignApp: Bool, forceTheme: PresentationTheme?, fromPublicChannel: Bool, segmentedValues: [ShareControllerSegmentedValue]?) { + init(environment: ShareControllerEnvironment, presentationData: PresentationData, presetText: String?, defaultAction: ShareControllerAction?, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, presentError: @escaping (String?, String) -> Void, externalShare: Bool, immediateExternalShare: Bool, immediatePeerId: PeerId?, fromForeignApp: Bool, forceTheme: PresentationTheme?, fromPublicChannel: Bool, segmentedValues: [ShareControllerSegmentedValue]?, shareStory: (() -> Void)?) { self.environment = environment self.presentationData = presentationData self.forceTheme = forceTheme @@ -318,6 +318,13 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate strongSelf.closePeerTopics(peer.peerId, selected: true) }) } + }, shareStory: shareStory.flatMap { shareStory in + return { [weak self] in + self?.animateOut(shared: false, completion: { [weak self] in + self?.dismiss?(false) + }) + shareStory() + } }) self.backgroundColor = nil diff --git a/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift b/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift index 6b0a824fb71..c48874744d8 100644 --- a/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift +++ b/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift @@ -20,10 +20,12 @@ final class ShareControllerInteraction { let togglePeer: (EngineRenderedPeer, Bool) -> Void let selectTopic: (EngineRenderedPeer, Int64, MessageHistoryThreadData) -> Void - - init(togglePeer: @escaping (EngineRenderedPeer, Bool) -> Void, selectTopic: @escaping (EngineRenderedPeer, Int64, MessageHistoryThreadData) -> Void) { + let shareStory: (() -> Void)? + + init(togglePeer: @escaping (EngineRenderedPeer, Bool) -> Void, selectTopic: @escaping (EngineRenderedPeer, Int64, MessageHistoryThreadData) -> Void, shareStory: (() -> Void)?) { self.togglePeer = togglePeer self.selectTopic = selectTopic + self.shareStory = shareStory } } @@ -91,28 +93,35 @@ final class ShareControllerGridSectionNode: ASDisplayNode { } final class ShareControllerPeerGridItem: GridItem { + enum ShareItem: Equatable { + case peer(peer: EngineRenderedPeer, presence: EnginePeer.Presence?, topicId: Int64?, threadData: MessageHistoryThreadData?) + case story + + var peerId: EnginePeer.Id? { + if case let .peer(peer, _, _, _) = self { + return peer.peerId + } else { + return nil + } + } + } + let environment: ShareControllerEnvironment let context: ShareControllerAccountContext let theme: PresentationTheme let strings: PresentationStrings - let peer: EngineRenderedPeer? - let presence: EnginePeer.Presence? - let topicId: Int64? - let threadData: MessageHistoryThreadData? + let item: ShareItem? let controllerInteraction: ShareControllerInteraction let search: Bool let section: GridSection? - init(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EngineRenderedPeer?, presence: EnginePeer.Presence?, topicId: Int64?, threadData: MessageHistoryThreadData?, controllerInteraction: ShareControllerInteraction, sectionTitle: String? = nil, search: Bool = false) { + init(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, theme: PresentationTheme, strings: PresentationStrings, item: ShareItem?, controllerInteraction: ShareControllerInteraction, sectionTitle: String? = nil, search: Bool = false) { self.environment = environment self.context = context self.theme = theme self.strings = strings - self.peer = peer - self.presence = presence - self.topicId = topicId - self.threadData = threadData + self.item = item self.controllerInteraction = controllerInteraction self.search = search @@ -126,7 +135,7 @@ final class ShareControllerPeerGridItem: GridItem { func node(layout: GridNodeLayout, synchronousLoad: Bool) -> GridItemNode { let node = ShareControllerPeerGridItemNode() node.controllerInteraction = self.controllerInteraction - node.setup(environment: self.environment, context: self.context, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, topicId: self.topicId, threadData: self.threadData, search: self.search, synchronousLoad: synchronousLoad, force: false) + node.setup(environment: self.environment, context: self.context, theme: self.theme, strings: self.strings, item: self.item, search: self.search, synchronousLoad: synchronousLoad, force: false) return node } @@ -136,12 +145,12 @@ final class ShareControllerPeerGridItem: GridItem { return } node.controllerInteraction = self.controllerInteraction - node.setup(environment: self.environment, context: self.context, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, topicId: self.topicId, threadData: self.threadData, search: self.search, synchronousLoad: false, force: false) + node.setup(environment: self.environment, context: self.context, theme: self.theme, strings: self.strings, item: self.item, search: self.search, synchronousLoad: false, force: false) } } final class ShareControllerPeerGridItemNode: GridItemNode { - private var currentState: (ShareControllerEnvironment, ShareControllerAccountContext, PresentationTheme, PresentationStrings, EngineRenderedPeer?, Bool, EnginePeer.Presence?, Int64?, MessageHistoryThreadData?)? + private var currentState: (environment: ShareControllerEnvironment, accountContext: ShareControllerAccountContext, theme: PresentationTheme, strings: PresentationStrings, item: ShareControllerPeerGridItem.ShareItem?, search: Bool)? private let peerNode: SelectablePeerNode private var presenceManager: PeerPresenceStatusManager? @@ -151,7 +160,11 @@ final class ShareControllerPeerGridItemNode: GridItemNode { private var absoluteLocation: (CGRect, CGSize)? var peerId: EnginePeer.Id? { - return self.currentState?.4?.peerId + if let item = self.currentState?.item, case let .peer(peer, _, _, _) = item { + return peer.peerId + } else { + return nil + } } override init() { @@ -161,9 +174,11 @@ final class ShareControllerPeerGridItemNode: GridItemNode { self.peerNode.toggleSelection = { [weak self] in if let strongSelf = self { - if let (_, _, _, _, maybePeer, search, _, _, _) = strongSelf.currentState, let peer = maybePeer { - if let _ = peer.peers[peer.peerId] { + if let (_, _, _, _, maybeItem, search) = strongSelf.currentState, let item = maybeItem { + if case let .peer(peer, _, _, _) = item, let _ = peer.peers[peer.peerId] { strongSelf.controllerInteraction?.togglePeer(peer, search) + } else if case .story = item { + strongSelf.controllerInteraction?.shareStory?() } } } @@ -173,7 +188,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode { guard let strongSelf = self, let currentState = strongSelf.currentState else { return } - strongSelf.setup(environment: currentState.0, context: currentState.1, theme: currentState.2, strings: currentState.3, peer: currentState.4, presence: currentState.6, topicId: currentState.7, threadData: currentState.8, search: currentState.5, synchronousLoad: false, force: true) + strongSelf.setup(environment: currentState.0, context: currentState.1, theme: currentState.2, strings: currentState.3, item: currentState.4, search: currentState.5, synchronousLoad: false, force: true) }) } @@ -185,21 +200,27 @@ final class ShareControllerPeerGridItemNode: GridItemNode { } } - func setup(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EngineRenderedPeer?, presence: EnginePeer.Presence?, topicId: Int64?, threadData: MessageHistoryThreadData?, search: Bool, synchronousLoad: Bool, force: Bool) { - if force || self.currentState == nil || self.currentState!.1 !== context || self.currentState!.3 !== theme || self.currentState!.4 != peer || self.currentState!.6 != presence || self.currentState!.7 != topicId { + func setup(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, theme: PresentationTheme, strings: PresentationStrings, item: ShareControllerPeerGridItem.ShareItem?, search: Bool, synchronousLoad: Bool, force: Bool) { + if force || self.currentState == nil || self.currentState!.1 !== context || self.currentState!.3 !== theme || self.currentState!.item != item { let itemTheme = SelectablePeerNodeTheme(textColor: theme.actionSheet.primaryTextColor, secretTextColor: theme.chatList.secretTitleColor, selectedTextColor: theme.actionSheet.controlAccentColor, checkBackgroundColor: theme.actionSheet.opaqueItemBackgroundColor, checkFillColor: theme.actionSheet.controlAccentColor, checkColor: theme.actionSheet.checkContentColor, avatarPlaceholderColor: theme.list.mediaPlaceholderColor) + var effectivePresence: EnginePeer.Presence? let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) - var online = false - if case let .user(peer) = peer?.peer, let presence = presence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != context.accountPeerId { - let relativeStatus = relativeUserPresenceStatus(presence, relativeTo: timestamp) - if case .online = relativeStatus { - online = true - } - } - self.peerNode.theme = itemTheme - if let peer = peer { + if let item, case let .peer(renderedPeer, presence, _, threadData) = item, let peer = renderedPeer.peer { + effectivePresence = presence + var isOnline = false + var isSupport = false + if case let .user(user) = peer, user.flags.contains(.isSupport) { + isSupport = true + } + if let presence, !peer.isService && !isSupport && peer.id != context.accountPeerId { + let relativeStatus = relativeUserPresenceStatus(presence, relativeTo: timestamp) + if case .online = relativeStatus { + isOnline = true + } + } + let resolveInlineStickers = context.resolveInlineStickers self.peerNode.setup( accountPeerId: context.accountPeerId, @@ -214,17 +235,26 @@ final class ShareControllerPeerGridItemNode: GridItemNode { }, theme: theme, strings: strings, - peer: peer, + peer: renderedPeer, customTitle: threadData?.info.title, iconId: threadData?.info.icon, iconColor: threadData?.info.iconColor ?? 0, - online: online, + online: isOnline, synchronousLoad: synchronousLoad ) if let shimmerNode = self.placeholderNode { self.placeholderNode = nil shimmerNode.removeFromSupernode() } + } else if let item, case .story = item { + self.peerNode.setupStoryRepost( + accountPeerId: context.accountPeerId, + postbox: context.stateManager.postbox, + network: context.stateManager.network, + theme: theme, + strings: strings, + synchronousLoad: synchronousLoad + ) } else { let shimmerNode: ShimmerEffectNode if let current = self.placeholderNode { @@ -252,10 +282,10 @@ final class ShareControllerPeerGridItemNode: GridItemNode { shimmerNode.update(backgroundColor: theme.list.itemBlocksBackgroundColor, foregroundColor: theme.list.mediaPlaceholderColor, shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: shapes, horizontal: true, size: self.bounds.size) } - self.currentState = (environment, context, theme, strings, peer, search, presence, topicId, threadData) + self.currentState = (environment, context, theme, strings, item, search) self.setNeedsLayout() - if let presence = presence { - self.presenceManager?.reset(presence: presence) + if let effectivePresence { + self.presenceManager?.reset(presence: effectivePresence) } } self.updateSelection(animated: false) @@ -263,8 +293,10 @@ final class ShareControllerPeerGridItemNode: GridItemNode { func updateSelection(animated: Bool) { var selected = false - if let controllerInteraction = self.controllerInteraction, let (_, _, _, _, maybePeer, _, _, _, _) = self.currentState, let peer = maybePeer { - selected = controllerInteraction.selectedPeerIds.contains(peer.peerId) + if let controllerInteraction = self.controllerInteraction, let (_, _, _, _, maybeItem, _) = self.currentState, let item = maybeItem { + if case let .peer(peer, _, _, _) = item { + selected = controllerInteraction.selectedPeerIds.contains(peer.peerId) + } } self.peerNode.updateSelection(selected: selected, animated: animated) @@ -277,7 +309,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode { self.peerNode.frame = bounds self.placeholderNode?.frame = bounds - if let (_, _, theme, _, _, _, _, _, _) = self.currentState, let shimmerNode = self.placeholderNode { + if let theme = self.currentState?.theme, let shimmerNode = self.placeholderNode { var shapes: [ShimmerEffectNode.Shape] = [] let titleLineWidth: CGFloat = 56.0 diff --git a/submodules/ShareController/Sources/SharePeersContainerNode.swift b/submodules/ShareController/Sources/SharePeersContainerNode.swift index 87b80936eb9..10c4b1d9316 100644 --- a/submodules/ShareController/Sources/SharePeersContainerNode.swift +++ b/submodules/ShareController/Sources/SharePeersContainerNode.swift @@ -37,31 +37,24 @@ extension CGPoint { private struct SharePeerEntry: Comparable, Identifiable { let index: Int32 - let peer: EngineRenderedPeer - let presence: EnginePeer.Presence? - let threadId: Int64? - let threadData: MessageHistoryThreadData? + let item: ShareControllerPeerGridItem.ShareItem let theme: PresentationTheme let strings: PresentationStrings var stableId: Int64 { - return self.peer.peerId.toInt64() + switch self.item { + case let .peer(peer, _, _, _): + return peer.peerId.toInt64() + case .story: + return 0 + } } static func ==(lhs: SharePeerEntry, rhs: SharePeerEntry) -> Bool { if lhs.index != rhs.index { return false } - if lhs.peer != rhs.peer { - return false - } - if lhs.presence != rhs.presence { - return false - } - if lhs.threadId != rhs.threadId { - return false - } - if lhs.threadData != rhs.threadData { + if lhs.item != rhs.item { return false } if lhs.theme !== rhs.theme { @@ -76,7 +69,7 @@ private struct SharePeerEntry: Comparable, Identifiable { } func item(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, interfaceInteraction: ShareControllerInteraction) -> GridItem { - return ShareControllerPeerGridItem(environment: environment, context: context, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, topicId: self.threadId, threadData: self.threadData, controllerInteraction: interfaceInteraction, search: false) + return ShareControllerPeerGridItem(environment: environment, context: context, theme: self.theme, strings: self.strings, item: self.item, controllerInteraction: interfaceInteraction, search: false) } } @@ -169,19 +162,26 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { self.peersValue.set(.single(peers)) + let canShareStory = controllerInteraction.shareStory != nil + let items: Signal<[SharePeerEntry], NoError> = combineLatest(self.peersValue.get(), self.foundPeers.get(), self.tick.get(), self.themePromise.get()) |> map { [weak controllerInteraction] initialPeers, foundPeers, _, theme -> [SharePeerEntry] in var entries: [SharePeerEntry] = [] var index: Int32 = 0 + if canShareStory { + entries.append(SharePeerEntry(index: index, item: .story, theme: theme, strings: strings)) + index += 1 + } + var existingPeerIds: Set = Set() - entries.append(SharePeerEntry(index: index, peer: EngineRenderedPeer(peer: accountPeer), presence: nil, threadId: nil, threadData: nil, theme: theme, strings: strings)) + entries.append(SharePeerEntry(index: index, item: .peer(peer: EngineRenderedPeer(peer: accountPeer), presence: nil, topicId: nil, threadData: nil), theme: theme, strings: strings)) existingPeerIds.insert(accountPeer.id) index += 1 for peer in foundPeers.reversed() { if !existingPeerIds.contains(peer.peerId) { - entries.append(SharePeerEntry(index: index, peer: peer, presence: nil, threadId: nil, threadData: nil, theme: theme, strings: strings)) + entries.append(SharePeerEntry(index: index, item: .peer(peer: peer, presence: nil, topicId: nil, threadData: nil), theme: theme, strings: strings)) existingPeerIds.insert(peer.peerId) index += 1 } @@ -190,7 +190,7 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { for (peer, presence) in initialPeers { if !existingPeerIds.contains(peer.peerId) { let thread = controllerInteraction?.selectedTopics[peer.peerId] - entries.append(SharePeerEntry(index: index, peer: peer, presence: presence, threadId: thread?.0, threadData: thread?.1, theme: theme, strings: strings)) + entries.append(SharePeerEntry(index: index, item: .peer(peer: peer, presence: presence, topicId: thread?.0, threadData: thread?.1), theme: theme, strings: strings)) existingPeerIds.insert(peer.peerId) index += 1 } @@ -568,7 +568,7 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { var scrollToItem: GridNodeScrollToItem? if let ensurePeerVisibleOnLayout = self.ensurePeerVisibleOnLayout { self.ensurePeerVisibleOnLayout = nil - if let index = self.entries.firstIndex(where: { $0.peer.peerId == ensurePeerVisibleOnLayout }) { + if let index = self.entries.firstIndex(where: { $0.item.peerId == ensurePeerVisibleOnLayout }) { scrollToItem = GridNodeScrollToItem(index: index, position: .visible, transition: transition, directionHint: .up, adjustForSection: false) } } diff --git a/submodules/ShareController/Sources/ShareSearchContainerNode.swift b/submodules/ShareController/Sources/ShareSearchContainerNode.swift index c612976c228..4db2fa54710 100644 --- a/submodules/ShareController/Sources/ShareSearchContainerNode.swift +++ b/submodules/ShareController/Sources/ShareSearchContainerNode.swift @@ -85,15 +85,15 @@ private enum ShareSearchRecentEntry: Comparable, Identifiable { func item(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, interfaceInteraction: ShareControllerInteraction) -> GridItem { switch self { - case let .topPeers(theme, strings): - return ShareControllerRecentPeersGridItem(environment: environment, context: context, theme: theme, strings: strings, controllerInteraction: interfaceInteraction) - case let .peer(_, theme, peer, associatedPeer, presence, strings): - var peers: [EnginePeer.Id: EnginePeer] = [peer.id: peer] - if let associatedPeer = associatedPeer { - peers[associatedPeer.id] = associatedPeer - } - let peer = EngineRenderedPeer(peerId: peer.id, peers: peers, associatedMedia: [:]) - return ShareControllerPeerGridItem(environment: environment, context: context, theme: theme, strings: strings, peer: peer, presence: presence, topicId: nil, threadData: nil, controllerInteraction: interfaceInteraction, sectionTitle: strings.DialogList_SearchSectionRecent, search: true) + case let .topPeers(theme, strings): + return ShareControllerRecentPeersGridItem(environment: environment, context: context, theme: theme, strings: strings, controllerInteraction: interfaceInteraction) + case let .peer(_, theme, peer, associatedPeer, presence, strings): + var peers: [EnginePeer.Id: EnginePeer] = [peer.id: peer] + if let associatedPeer = associatedPeer { + peers[associatedPeer.id] = associatedPeer + } + let peer = EngineRenderedPeer(peerId: peer.id, peers: peers, associatedMedia: [:]) + return ShareControllerPeerGridItem(environment: environment, context: context, theme: theme, strings: strings, item: .peer(peer: peer, presence: presence, topicId: nil, threadData: nil), controllerInteraction: interfaceInteraction, sectionTitle: strings.DialogList_SearchSectionRecent, search: true) } } } @@ -131,7 +131,9 @@ private struct ShareSearchPeerEntry: Comparable, Identifiable { } func item(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, interfaceInteraction: ShareControllerInteraction) -> GridItem { - return ShareControllerPeerGridItem(environment: environment, context: context, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, topicId: nil, threadData: nil, controllerInteraction: interfaceInteraction, search: true) +// let item: ShareControllerPeerGridItem.ShareItem +// item = self.peer.flatMap { .peer(peer: $0, presence: self.presence, topicId: nil, threadData: nil) } + return ShareControllerPeerGridItem(environment: environment, context: context, theme: self.theme, strings: self.strings, item: self.peer.flatMap({ .peer(peer: $0, presence: self.presence, topicId: nil, threadData: nil) }), controllerInteraction: interfaceInteraction, search: true) } } diff --git a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift index b8ae91d6813..31a05c84d98 100644 --- a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift +++ b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift @@ -244,6 +244,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode { } } + public var animationLoopTime: Double = 4.0 private var animationTimer: SwiftSignalKit.Timer? public var animation: String? { didSet { @@ -268,7 +269,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode { Queue.mainQueue().after(1.25) { self.animationNode?.play() - let timer = SwiftSignalKit.Timer(timeout: 4.0, repeat: true, completion: { [weak self] in + let timer = SwiftSignalKit.Timer(timeout: self.animationLoopTime, repeat: true, completion: { [weak self] in self?.animationNode?.play() }, queue: Queue.mainQueue()) self.animationTimer = timer @@ -276,7 +277,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode { } } else { self.animationNode?.play() - let timer = SwiftSignalKit.Timer(timeout: 4.0, repeat: true, completion: { [weak self] in + let timer = SwiftSignalKit.Timer(timeout: self.animationLoopTime, repeat: true, completion: { [weak self] in self?.animationNode?.play() }, queue: Queue.mainQueue()) self.animationTimer = timer @@ -377,6 +378,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode { self.iconNode = ASImageNode() self.iconNode.displaysAsynchronously = false self.iconNode.image = generateTintedImage(image: icon, color: self.theme.foregroundColor) + self.iconNode.isUserInteractionEnabled = false super.init() @@ -981,6 +983,7 @@ public final class SolidRoundedButtonView: UIView { } } + public var animationLoopTime: Double = 4.0 private var animationTimer: SwiftSignalKit.Timer? public var animation: String? { didSet { @@ -1005,7 +1008,7 @@ public final class SolidRoundedButtonView: UIView { Queue.mainQueue().after(1.25) { self.animationNode?.play() - let timer = SwiftSignalKit.Timer(timeout: 4.0, repeat: true, completion: { [weak self] in + let timer = SwiftSignalKit.Timer(timeout: self.animationLoopTime, repeat: true, completion: { [weak self] in self?.animationNode?.play() }, queue: Queue.mainQueue()) self.animationTimer = timer @@ -1013,7 +1016,7 @@ public final class SolidRoundedButtonView: UIView { } } else { self.animationNode?.play() - let timer = SwiftSignalKit.Timer(timeout: 4.0, repeat: true, completion: { [weak self] in + let timer = SwiftSignalKit.Timer(timeout: self.animationLoopTime, repeat: true, completion: { [weak self] in self?.animationNode?.play() }, queue: Queue.mainQueue()) self.animationTimer = timer diff --git a/submodules/StatisticsUI/BUILD b/submodules/StatisticsUI/BUILD index 94f62712b7a..4246307631a 100644 --- a/submodules/StatisticsUI/BUILD +++ b/submodules/StatisticsUI/BUILD @@ -13,6 +13,7 @@ swift_library( "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", + "//submodules/ComponentFlow", "//submodules/Postbox:Postbox", "//submodules/TelegramCore:TelegramCore", "//submodules/TelegramPresentationData:TelegramPresentationData", @@ -35,6 +36,8 @@ swift_library( "//submodules/PremiumUI:PremiumUI", "//submodules/InviteLinksUI:InviteLinksUI", "//submodules/ShareController:ShareController", + "//submodules/TelegramUI/Components/Stories/AvatarStoryIndicatorComponent", + "//submodules/TelegramUI/Components/Stories/StoryContainerScreen", ], visibility = [ "//visibility:public", diff --git a/submodules/StatisticsUI/Sources/ChannelStatsController.swift b/submodules/StatisticsUI/Sources/ChannelStatsController.swift index 82e7b01c0e0..cfeacb85eed 100644 --- a/submodules/StatisticsUI/Sources/ChannelStatsController.swift +++ b/submodules/StatisticsUI/Sources/ChannelStatsController.swift @@ -21,13 +21,15 @@ import UndoUI import ShareController import ItemListPeerActionItem import PremiumUI +import StoryContainerScreen private let initialBoostersDisplayedLimit: Int32 = 5 private final class ChannelStatsControllerArguments { let context: AccountContext let loadDetailedGraph: (StatsGraph, Int64) -> Signal - let openMessageStats: (MessageId) -> Void + let openPostStats: (EnginePeer, StatsPostItem) -> Void + let openStory: (EngineStoryItem, UIView) -> Void let contextAction: (MessageId, ASDisplayNode, ContextGesture?) -> Void let copyBoostLink: (String) -> Void let shareBoostLink: (String) -> Void @@ -37,10 +39,11 @@ private final class ChannelStatsControllerArguments { let createPrepaidGiveaway: (PrepaidGiveaway) -> Void let updateGiftsSelected: (Bool) -> Void - init(context: AccountContext, loadDetailedGraph: @escaping (StatsGraph, Int64) -> Signal, openMessage: @escaping (MessageId) -> Void, contextAction: @escaping (MessageId, ASDisplayNode, ContextGesture?) -> Void, copyBoostLink: @escaping (String) -> Void, shareBoostLink: @escaping (String) -> Void, openBoost: @escaping (ChannelBoostersContext.State.Boost) -> Void, expandBoosters: @escaping () -> Void, openGifts: @escaping () -> Void, createPrepaidGiveaway: @escaping (PrepaidGiveaway) -> Void, updateGiftsSelected: @escaping (Bool) -> Void) { + init(context: AccountContext, loadDetailedGraph: @escaping (StatsGraph, Int64) -> Signal, openPostStats: @escaping (EnginePeer, StatsPostItem) -> Void, openStory: @escaping (EngineStoryItem, UIView) -> Void, contextAction: @escaping (MessageId, ASDisplayNode, ContextGesture?) -> Void, copyBoostLink: @escaping (String) -> Void, shareBoostLink: @escaping (String) -> Void, openBoost: @escaping (ChannelBoostersContext.State.Boost) -> Void, expandBoosters: @escaping () -> Void, openGifts: @escaping () -> Void, createPrepaidGiveaway: @escaping (PrepaidGiveaway) -> Void, updateGiftsSelected: @escaping (Bool) -> Void) { self.context = context self.loadDetailedGraph = loadDetailedGraph - self.openMessageStats = openMessage + self.openPostStats = openPostStats + self.openStory = openStory self.contextAction = contextAction self.copyBoostLink = copyBoostLink self.shareBoostLink = shareBoostLink @@ -62,8 +65,11 @@ private enum StatsSection: Int32 { case followersBySource case languages case postInteractions - case recentPosts case instantPageInteractions + case reactionsByEmotion + case storyInteractions + case storyReactionsByEmotion + case recentPosts case boostLevel case boostOverview case boostPrepaid @@ -72,6 +78,45 @@ private enum StatsSection: Int32 { case gifts } +enum StatsPostItem: Equatable { + static func == (lhs: StatsPostItem, rhs: StatsPostItem) -> Bool { + switch lhs { + case let .message(lhsMessage): + if case let .message(rhsMessage) = rhs { + return lhsMessage.id == rhsMessage.id + } else { + return false + } + case let .story(lhsPeer, lhsStory): + if case let .story(rhsPeer, rhsStory) = rhs, lhsPeer == rhsPeer, lhsStory == rhsStory { + return true + } else { + return false + } + } + } + + case message(Message) + case story(EnginePeer, EngineStoryItem) + + var isStory: Bool { + if case .story = self { + return true + } else { + return false + } + } + + var timestamp: Int32 { + switch self { + case let .message(message): + return message.timestamp + case let .story(_, story): + return story.timestamp + } + } +} + private enum StatsEntry: ItemListNodeEntry { case overviewTitle(PresentationTheme, String, String) case overview(PresentationTheme, ChannelStats) @@ -99,13 +144,22 @@ private enum StatsEntry: ItemListNodeEntry { case postInteractionsTitle(PresentationTheme, String) case postInteractionsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType) + + case reactionsByEmotionTitle(PresentationTheme, String) + case reactionsByEmotionGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType) - case postsTitle(PresentationTheme, String) - case post(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Message, ChannelStatsMessageInteractions) + case storyInteractionsTitle(PresentationTheme, String) + case storyInteractionsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType) + + case storyReactionsByEmotionTitle(PresentationTheme, String) + case storyReactionsByEmotionGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType) case instantPageInteractionsTitle(PresentationTheme, String) case instantPageInteractionsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType) + case postsTitle(PresentationTheme, String) + case post(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, StatsPostItem, ChannelStatsPostInteractions) + case boostLevel(PresentationTheme, Int32, Int32, CGFloat) case boostOverviewTitle(PresentationTheme, String) @@ -149,10 +203,16 @@ private enum StatsEntry: ItemListNodeEntry { return StatsSection.languages.rawValue case .postInteractionsTitle, .postInteractionsGraph: return StatsSection.postInteractions.rawValue - case .postsTitle, .post: - return StatsSection.recentPosts.rawValue case .instantPageInteractionsTitle, .instantPageInteractionsGraph: return StatsSection.instantPageInteractions.rawValue + case .reactionsByEmotionTitle, .reactionsByEmotionGraph: + return StatsSection.reactionsByEmotion.rawValue + case .storyInteractionsTitle, .storyInteractionsGraph: + return StatsSection.storyInteractions.rawValue + case .storyReactionsByEmotionTitle, .storyReactionsByEmotionGraph: + return StatsSection.storyReactionsByEmotion.rawValue + case .postsTitle, .post: + return StatsSection.recentPosts.rawValue case .boostLevel: return StatsSection.boostLevel.rawValue case .boostOverviewTitle, .boostOverview: @@ -207,13 +267,25 @@ private enum StatsEntry: ItemListNodeEntry { case .postInteractionsGraph: return 17 case .instantPageInteractionsTitle: - return 18 - case .instantPageInteractionsGraph: - return 19 - case .postsTitle: + return 18 + case .instantPageInteractionsGraph: + return 19 + case .reactionsByEmotionTitle: return 20 - case let .post(index, _, _, _, _, _): - return 21 + index + case .reactionsByEmotionGraph: + return 21 + case .storyInteractionsTitle: + return 22 + case .storyInteractionsGraph: + return 23 + case .storyReactionsByEmotionTitle: + return 24 + case .storyReactionsByEmotionGraph: + return 25 + case .postsTitle: + return 26 + case let .post(index, _, _, _, _, _, _): + return 27 + index case .boostLevel: return 2000 case .boostOverviewTitle: @@ -367,12 +439,6 @@ private enum StatsEntry: ItemListNodeEntry { } else { return false } - case let .post(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsMessage, lhsInteractions): - if case let .post(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsMessage, rhsInteractions) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsMessage.id == rhsMessage.id, lhsInteractions == rhsInteractions { - return true - } else { - return false - } case let .instantPageInteractionsTitle(lhsTheme, lhsText): if case let .instantPageInteractionsTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { return true @@ -385,6 +451,48 @@ private enum StatsEntry: ItemListNodeEntry { } else { return false } + case let .reactionsByEmotionTitle(lhsTheme, lhsText): + if case let .reactionsByEmotionTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } + case let .reactionsByEmotionGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType): + if case let .reactionsByEmotionGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType { + return true + } else { + return false + } + case let .storyInteractionsTitle(lhsTheme, lhsText): + if case let .storyInteractionsTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } + case let .storyInteractionsGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType): + if case let .storyInteractionsGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType { + return true + } else { + return false + } + case let .storyReactionsByEmotionTitle(lhsTheme, lhsText): + if case let .storyReactionsByEmotionTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } + case let .storyReactionsByEmotionGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType): + if case let .storyReactionsByEmotionGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType { + return true + } else { + return false + } + case let .post(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsPost, lhsInteractions): + if case let .post(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsPost, rhsInteractions) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, arePeersEqual(lhsPeer, rhsPeer), lhsPost == rhsPost, lhsInteractions == rhsInteractions { + return true + } else { + return false + } case let .boostLevel(lhsTheme, lhsBoosts, lhsLevel, lhsPosition): if case let .boostLevel(rhsTheme, rhsBoosts, rhsLevel, rhsPosition) = rhs, lhsTheme === rhsTheme, lhsBoosts == rhsBoosts, lhsLevel == rhsLevel, lhsPosition == rhsPosition { return true @@ -507,8 +615,11 @@ private enum StatsEntry: ItemListNodeEntry { let .followersBySourceTitle(_, text), let .languagesTitle(_, text), let .postInteractionsTitle(_, text), - let .postsTitle(_, text), let .instantPageInteractionsTitle(_, text), + let .reactionsByEmotionTitle(_, text), + let .storyInteractionsTitle(_, text), + let .storyReactionsByEmotionTitle(_, text), + let .postsTitle(_, text), let .boostOverviewTitle(_, text), let .boostPrepaidTitle(_, text), let .boostersTitle(_, text), @@ -527,10 +638,13 @@ private enum StatsEntry: ItemListNodeEntry { let .viewsByHourGraph(_, _, _, graph, type), let .viewsBySourceGraph(_, _, _, graph, type), let .followersBySourceGraph(_, _, _, graph, type), - let .languagesGraph(_, _, _, graph, type): + let .languagesGraph(_, _, _, graph, type), + let .reactionsByEmotionGraph(_, _, _, graph, type), + let .storyReactionsByEmotionGraph(_, _, _, graph, type): return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, sectionId: self.section, style: .blocks) case let .postInteractionsGraph(_, _, _, graph, type), - let .instantPageInteractionsGraph(_, _, _, graph, type): + let .instantPageInteractionsGraph(_, _, _, graph, type), + let .storyInteractionsGraph(_, _, _, graph, type): return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, getDetailsData: { date, completion in let _ = arguments.loadDetailedGraph(graph, Int64(date.timeIntervalSince1970) * 1000).start(next: { graph in if let graph = graph, case let .Loaded(_, data) = graph { @@ -538,12 +652,18 @@ private enum StatsEntry: ItemListNodeEntry { } }) }, sectionId: self.section, style: .blocks) - case let .post(_, _, _, _, message, interactions): - return StatsMessageItem(context: arguments.context, presentationData: presentationData, message: message, views: interactions.views, forwards: interactions.forwards, sectionId: self.section, style: .blocks, action: { - arguments.openMessageStats(message.id) - }, contextAction: { node, gesture in - arguments.contextAction(message.id, node, gesture) - }) + case let .post(_, _, _, _, peer, post, interactions): + return StatsMessageItem(context: arguments.context, presentationData: presentationData, peer: peer, item: post, views: interactions.views, reactions: interactions.reactions, forwards: interactions.forwards, sectionId: self.section, style: .blocks, action: { + arguments.openPostStats(EnginePeer(peer), post) + }, openStory: { sourceView in + if case let .story(_, story) = post { + arguments.openStory(story, sourceView) + } + }, contextAction: !post.isStory ? { node, gesture in + if case let .message(message) = post { + arguments.contextAction(message.id, node, gesture) + } + } : nil) case let .boosterTabs(_, boostText, giftText, giftSelected): return BoostsTabsItem(theme: presentationData.theme, boostsText: boostText, giftsText: giftText, selectedTab: giftSelected ? .gifts : .boosts, sectionId: self.section, selectionUpdated: { tab in arguments.updateGiftsSelected(tab == .gifts) @@ -703,7 +823,7 @@ private struct ChannelStatsControllerState: Equatable { } -private func channelStatsControllerEntries(state: ChannelStatsControllerState, peer: EnginePeer?, data: ChannelStats?, messages: [Message]?, interactions: [MessageId: ChannelStatsMessageInteractions]?, boostData: ChannelBoostStatus?, boostersState: ChannelBoostersContext.State?, giftsState: ChannelBoostersContext.State?, presentationData: PresentationData, giveawayAvailable: Bool) -> [StatsEntry] { +private func channelStatsControllerEntries(state: ChannelStatsControllerState, peer: EnginePeer?, data: ChannelStats?, messages: [Message]?, stories: PeerStoryListContext.State?, interactions: [ChannelStatsPostInteractions.PostId: ChannelStatsPostInteractions]?, boostData: ChannelBoostStatus?, boostersState: ChannelBoostersContext.State?, giftsState: ChannelBoostersContext.State?, presentationData: PresentationData, giveawayAvailable: Bool) -> [StatsEntry] { var entries: [StatsEntry] = [] switch state.section { @@ -760,12 +880,54 @@ private func channelStatsControllerEntries(state: ChannelStatsControllerState, p entries.append(.instantPageInteractionsGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.instantPageInteractionsGraph, .twoAxisStep)) } - if let messages = messages, !messages.isEmpty, let interactions = interactions, !interactions.isEmpty { - entries.append(.postsTitle(presentationData.theme, presentationData.strings.Stats_PostsTitle)) - var index: Int32 = 0 - for message in messages { - if let interactions = interactions[message.id] { - entries.append(.post(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, message, interactions)) + if !data.reactionsByEmotionGraph.isEmpty { + entries.append(.reactionsByEmotionTitle(presentationData.theme, presentationData.strings.Stats_ReactionsByEmotionTitle)) + entries.append(.reactionsByEmotionGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.reactionsByEmotionGraph, .bars)) + } + + if !data.storyInteractionsGraph.isEmpty { + entries.append(.storyInteractionsTitle(presentationData.theme, presentationData.strings.Stats_StoryInteractionsTitle)) + entries.append(.storyInteractionsGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.storyInteractionsGraph, .twoAxisStep)) + } + + if !data.storyReactionsByEmotionGraph.isEmpty { + entries.append(.storyReactionsByEmotionTitle(presentationData.theme, presentationData.strings.Stats_StoryReactionsByEmotionTitle)) + entries.append(.storyReactionsByEmotionGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.storyReactionsByEmotionGraph, .bars)) + } + + + if let peer, let interactions { + var posts: [StatsPostItem] = [] + if let messages { + for message in messages { + if let _ = interactions[.message(id: message.id)] { + posts.append(.message(message)) + } + } + } + if let stories { + for story in stories.items { + if let _ = interactions[.story(peerId: peer.id, id: story.id)] { + posts.append(.story(peer, story)) + } + } + } + posts.sort(by: { $0.timestamp > $1.timestamp }) + + if !posts.isEmpty { + entries.append(.postsTitle(presentationData.theme, presentationData.strings.Stats_PostsTitle)) + var index: Int32 = 0 + for post in posts { + switch post { + case let .message(message): + if let interactions = interactions[.message(id: message.id)] { + entries.append(.post(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer._asPeer(), post, interactions)) + } + case let .story(_, story): + if let interactions = interactions[.story(peerId: peer.id, id: story.id)] { + entries.append(.post(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer._asPeer(), post, interactions)) + } + } index += 1 } } @@ -883,7 +1045,7 @@ private func channelStatsControllerEntries(state: ChannelStatsControllerState, p return entries } -public func channelStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: PeerId, section: ChannelStatsSection = .stats, boostStatus: ChannelBoostStatus? = nil, statsDatacenterId: Int32?) -> ViewController { +public func channelStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: PeerId, section: ChannelStatsSection = .stats, boostStatus: ChannelBoostStatus? = nil) -> ViewController { let statePromise = ValuePromise(ChannelStatsControllerState(section: section, boostersExpanded: false, moreBoostersDisplayed: 0, giftsSelected: false), ignoreRepeated: true) let stateValue = Atomic(value: ChannelStatsControllerState(section: section, boostersExpanded: false, moreBoostersDisplayed: 0, giftsSelected: false)) let updateState: ((ChannelStatsControllerState) -> ChannelStatsControllerState) -> Void = { f in @@ -892,16 +1054,17 @@ public func channelStatsController(context: AccountContext, updatedPresentationD let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) - var openMessageStatsImpl: ((MessageId) -> Void)? + var openPostStatsImpl: ((EnginePeer, StatsPostItem) -> Void)? + var openStoryImpl: ((EngineStoryItem, UIView) -> Void)? var contextActionImpl: ((MessageId, ASDisplayNode, ContextGesture?) -> Void)? let actionsDisposable = DisposableSet() let dataPromise = Promise(nil) let messagesPromise = Promise(nil) - let datacenterId: Int32 = statsDatacenterId ?? 0 - - let statsContext = ChannelStatsContext(postbox: context.account.postbox, network: context.account.network, datacenterId: datacenterId, peerId: peerId) + let storiesPromise = Promise() + + let statsContext = ChannelStatsContext(postbox: context.account.postbox, network: context.account.network, peerId: peerId) let dataSignal: Signal = statsContext.state |> map { state in return state.stats @@ -915,6 +1078,9 @@ public func channelStatsController(context: AccountContext, updatedPresentationD statsContext.loadViewsBySourceGraph() statsContext.loadLanguagesGraph() statsContext.loadInstantPageInteractionsGraph() + statsContext.loadReactionsByEmotionGraph() + statsContext.loadStoryInteractionsGraph() + statsContext.loadStoryReactionsByEmotionGraph() } } }) @@ -937,8 +1103,10 @@ public func channelStatsController(context: AccountContext, updatedPresentationD let arguments = ChannelStatsControllerArguments(context: context, loadDetailedGraph: { graph, x -> Signal in return statsContext.loadDetailedGraph(graph, x: x) - }, openMessage: { messageId in - openMessageStatsImpl?(messageId) + }, openPostStats: { peer, item in + openPostStatsImpl?(peer, item) + }, openStory: { story, sourceView in + openStoryImpl?(story, sourceView) }, contextAction: { messageId, node, gesture in contextActionImpl?(messageId, node, gesture) }, copyBoostLink: { link in @@ -1042,12 +1210,22 @@ public func channelStatsController(context: AccountContext, updatedPresentationD updateState { $0.withUpdatedGiftsSelected(selected).withUpdatedBoostersExpanded(false) } }) - let messageView = context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId, threadId: nil), index: .upperBound, anchorIndex: .upperBound, count: 100, fixedCombinedReadStates: nil) + let messageView = context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId, threadId: nil), index: .upperBound, anchorIndex: .upperBound, count: 200, fixedCombinedReadStates: nil) |> map { messageHistoryView, _, _ -> MessageHistoryView? in return messageHistoryView } messagesPromise.set(.single(nil) |> then(messageView)) + let storyList = PeerStoryListContext(account: context.account, peerId: peerId, isArchived: false) + storyList.loadMore() + storiesPromise.set( + .single(nil) + |> then( + storyList.state + |> map(Optional.init) + ) + ) + let longLoadingSignal: Signal = .single(false) |> then(.single(true) |> delay(2.0, queue: Queue.mainQueue())) let previousData = Atomic(value: nil) @@ -1059,13 +1237,14 @@ public func channelStatsController(context: AccountContext, updatedPresentationD context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)), dataPromise.get(), messagesPromise.get(), + storiesPromise.get(), boostData, boostsContext.state, giftsContext.state, longLoadingSignal ) |> deliverOnMainQueue - |> map { presentationData, state, peer, data, messageView, boostData, boostersState, giftsState, longLoading -> (ItemListControllerState, (ItemListNodeState, Any)) in + |> map { presentationData, state, peer, data, messageView, stories, boostData, boostersState, giftsState, longLoading -> (ItemListControllerState, (ItemListNodeState, Any)) in let previous = previousData.swap(data) var emptyStateItem: ItemListControllerEmptyStateItem? switch state.section { @@ -1083,23 +1262,36 @@ public func channelStatsController(context: AccountContext, updatedPresentationD } } - let messages = messageView?.entries.map { $0.message }.sorted(by: { (lhsMessage, rhsMessage) -> Bool in + var existingGroupingKeys = Set() + var idsToFilter = Set() + var messages = messageView?.entries.map { $0.message } ?? [] + for message in messages { + if let groupingKey = message.groupingKey { + if existingGroupingKeys.contains(groupingKey) { + idsToFilter.insert(message.id) + } else { + existingGroupingKeys.insert(groupingKey) + } + } + } + messages = messages.filter { !idsToFilter.contains($0.id) }.sorted(by: { (lhsMessage, rhsMessage) -> Bool in return lhsMessage.timestamp > rhsMessage.timestamp }) - let interactions = data?.messageInteractions.reduce([MessageId : ChannelStatsMessageInteractions]()) { (map, interactions) -> [MessageId : ChannelStatsMessageInteractions] in + let interactions = data?.postInteractions.reduce([ChannelStatsPostInteractions.PostId : ChannelStatsPostInteractions]()) { (map, interactions) -> [ChannelStatsPostInteractions.PostId : ChannelStatsPostInteractions] in var map = map - map[interactions.messageId] = interactions + map[interactions.postId] = interactions return map } let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .sectionControl([presentationData.strings.Stats_Statistics, presentationData.strings.Stats_Boosts], state.section == .boosts ? 1 : 0), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) - let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelStatsControllerEntries(state: state, peer: peer, data: data, messages: messages, interactions: interactions, boostData: boostData, boostersState: boostersState, giftsState: giftsState, presentationData: presentationData, giveawayAvailable: premiumConfiguration.giveawayGiftsPurchaseAvailable), style: .blocks, emptyStateItem: emptyStateItem, crossfadeState: previous == nil, animateChanges: false) + let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelStatsControllerEntries(state: state, peer: peer, data: data, messages: messages, stories: stories, interactions: interactions, boostData: boostData, boostersState: boostersState, giftsState: giftsState, presentationData: presentationData, giveawayAvailable: premiumConfiguration.giveawayGiftsPurchaseAvailable), style: .blocks, emptyStateItem: emptyStateItem, crossfadeState: previous == nil, animateChanges: false) return (controllerState, (listState, arguments)) } |> afterDisposed { actionsDisposable.dispose() let _ = statsContext.state + let _ = storyList.state } let controller = ItemListController(context: context, state: signal) @@ -1116,8 +1308,75 @@ public func channelStatsController(context: AccountContext, updatedPresentationD controller.didDisappear = { [weak controller] _ in controller?.clearItemNodesHighlight(animated: true) } - openMessageStatsImpl = { [weak controller] messageId in - controller?.push(messageStatsController(context: context, messageId: messageId, statsDatacenterId: statsDatacenterId)) + openPostStatsImpl = { [weak controller] peer, post in + let subject: StatsSubject + switch post { + case let .message(message): + subject = .message(id: message.id) + case let .story(_, story): + subject = .story(peerId: peerId, id: story.id, item: story, fromStory: false) + } + controller?.push(messageStatsController(context: context, subject: subject)) + } + openStoryImpl = { [weak controller] story, sourceView in + let storyContent = SingleStoryContentContextImpl(context: context, storyId: StoryId(peerId: peerId, id: story.id), storyItem: story, readGlobally: false) + let _ = (storyContent.state + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { [weak controller, weak sourceView] _ in + guard let controller, let sourceView else { + return + } + let transitionIn = StoryContainerScreen.TransitionIn( + sourceView: sourceView, + sourceRect: sourceView.bounds, + sourceCornerRadius: sourceView.bounds.width * 0.5, + sourceIsAvatar: false + ) + + let storyContainerScreen = StoryContainerScreen( + context: context, + content: storyContent, + transitionIn: transitionIn, + transitionOut: { [weak sourceView] peerId, storyIdValue in + if let sourceView { + let destinationView = sourceView + return StoryContainerScreen.TransitionOut( + destinationView: destinationView, + transitionView: StoryContainerScreen.TransitionView( + makeView: { [weak destinationView] in + let parentView = UIView() + if let copyView = destinationView?.snapshotContentTree(unhide: true) { + parentView.addSubview(copyView) + } + return parentView + }, + updateView: { copyView, state, transition in + guard let view = copyView.subviews.first else { + return + } + let size = state.sourceSize.interpolate(to: state.destinationSize, amount: state.progress) + transition.setPosition(view: view, position: CGPoint(x: size.width * 0.5, y: size.height * 0.5)) + transition.setScale(view: view, scale: size.width / state.destinationSize.width) + }, + insertCloneTransitionView: nil + ), + destinationRect: destinationView.bounds, + destinationCornerRadius: destinationView.bounds.width * 0.5, + destinationIsAvatar: false, + completed: { [weak sourceView] in + guard let sourceView else { + return + } + sourceView.isHidden = false + } + ) + } else { + return nil + } + } + ) + controller.push(storyContainerScreen) + }) } contextActionImpl = { [weak controller] messageId, sourceNode, gesture in guard let controller = controller, let sourceNode = sourceNode as? ContextExtractedContentContainingNode else { diff --git a/submodules/StatisticsUI/Sources/GroupStatsController.swift b/submodules/StatisticsUI/Sources/GroupStatsController.swift index 32b9a455693..452a4c8aa0b 100644 --- a/submodules/StatisticsUI/Sources/GroupStatsController.swift +++ b/submodules/StatisticsUI/Sources/GroupStatsController.swift @@ -707,7 +707,7 @@ private func canEditAdminRights(accountPeerId: EnginePeer.Id, channelPeer: Engin } } -public func groupStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: EnginePeer.Id, statsDatacenterId: Int32?) -> ViewController { +public func groupStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: EnginePeer.Id) -> ViewController { let statePromise = ValuePromise(GroupStatsState()) let stateValue = Atomic(value: GroupStatsState()) let updateState: ((GroupStatsState) -> GroupStatsState) -> Void = { f in @@ -718,8 +718,6 @@ public func groupStatsController(context: AccountContext, updatedPresentationDat let dataPromise = Promise(nil) let peersPromise = Promise<[EnginePeer.Id: EnginePeer]?>(nil) - let datacenterId: Int32 = statsDatacenterId ?? 0 - var openPeerImpl: ((EnginePeer) -> Void)? var openPeerHistoryImpl: ((EnginePeer.Id) -> Void)? var openPeerAdminActionsImpl: ((EnginePeer.Id) -> Void)? @@ -727,7 +725,7 @@ public func groupStatsController(context: AccountContext, updatedPresentationDat actionsDisposable.add(context.account.viewTracker.peerView(peerId, updateData: true).start()) - let statsContext = GroupStatsContext(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, datacenterId: datacenterId, peerId: peerId) + let statsContext = GroupStatsContext(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId) let dataSignal: Signal = statsContext.state |> map { state in return state.stats diff --git a/submodules/StatisticsUI/Sources/MessageStatsController.swift b/submodules/StatisticsUI/Sources/MessageStatsController.swift index e4bef176be2..de7cdbc9cd5 100644 --- a/submodules/StatisticsUI/Sources/MessageStatsController.swift +++ b/submodules/StatisticsUI/Sources/MessageStatsController.swift @@ -2,6 +2,8 @@ import Foundation import UIKit import Display import SwiftSignalKit +import AsyncDisplayKit +import Postbox import TelegramCore import TelegramPresentationData import TelegramUIPreferences @@ -13,34 +15,41 @@ import AccountContext import PresentationDataUtils import AppBundle import GraphUI +import StoryContainerScreen private final class MessageStatsControllerArguments { let context: AccountContext let loadDetailedGraph: (StatsGraph, Int64) -> Signal let openMessage: (EngineMessage.Id) -> Void + let openStory: (EnginePeer.Id, EngineStoryItem, UIView) -> Void - init(context: AccountContext, loadDetailedGraph: @escaping (StatsGraph, Int64) -> Signal, openMessage: @escaping (EngineMessage.Id) -> Void) { + init(context: AccountContext, loadDetailedGraph: @escaping (StatsGraph, Int64) -> Signal, openMessage: @escaping (EngineMessage.Id) -> Void, openStory: @escaping (EnginePeer.Id, EngineStoryItem, UIView) -> Void) { self.context = context self.loadDetailedGraph = loadDetailedGraph self.openMessage = openMessage + self.openStory = openStory } } private enum StatsSection: Int32 { case overview case interactions + case reactions case publicForwards } private enum StatsEntry: ItemListNodeEntry { case overviewTitle(PresentationTheme, String) - case overview(PresentationTheme, MessageStats, Int32?) + case overview(PresentationTheme, PostStats, EngineStoryItem.Views?, Int32?) case interactionsTitle(PresentationTheme, String) - case interactionsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType) + case interactionsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType, Bool) + + case reactionsTitle(PresentationTheme, String) + case reactionsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType, Bool) case publicForwardsTitle(PresentationTheme, String) - case publicForward(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, EngineMessage) + case publicForward(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsPostItem) var section: ItemListSectionId { switch self { @@ -48,6 +57,8 @@ private enum StatsEntry: ItemListNodeEntry { return StatsSection.overview.rawValue case .interactionsTitle, .interactionsGraph: return StatsSection.interactions.rawValue + case .reactionsTitle, .reactionsGraph: + return StatsSection.reactions.rawValue case .publicForwardsTitle, .publicForward: return StatsSection.publicForwards.rawValue } @@ -63,10 +74,14 @@ private enum StatsEntry: ItemListNodeEntry { return 2 case .interactionsGraph: return 3 - case .publicForwardsTitle: + case .reactionsTitle: return 4 + case .reactionsGraph: + return 5 + case .publicForwardsTitle: + return 6 case let .publicForward(index, _, _, _, _): - return 5 + index + return 7 + index } } @@ -78,9 +93,15 @@ private enum StatsEntry: ItemListNodeEntry { } else { return false } - case let .overview(lhsTheme, lhsStats, lhsPublicShares): - if case let .overview(rhsTheme, rhsStats, rhsPublicShares) = rhs, lhsTheme === rhsTheme, lhsStats == rhsStats, lhsPublicShares == rhsPublicShares { - return true + case let .overview(lhsTheme, lhsStats, lhsViews, lhsPublicShares): + if case let .overview(rhsTheme, rhsStats, rhsViews, rhsPublicShares) = rhs, lhsTheme === rhsTheme, lhsViews == rhsViews, lhsPublicShares == rhsPublicShares { + if let lhsMessageStats = lhsStats as? MessageStats, let rhsMessageStats = rhsStats as? MessageStats { + return lhsMessageStats == rhsMessageStats + } else if let lhsStoryStats = lhsStats as? StoryStats, let rhsStoryStats = rhsStats as? StoryStats { + return lhsStoryStats == rhsStoryStats + } else { + return false + } } else { return false } @@ -90,8 +111,20 @@ private enum StatsEntry: ItemListNodeEntry { } else { return false } - case let .interactionsGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType): - if case let .interactionsGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType { + case let .interactionsGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType, lhsNoInitialZoom): + if case let .interactionsGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType, rhsNoInitialZoom) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType, lhsNoInitialZoom == rhsNoInitialZoom { + return true + } else { + return false + } + case let .reactionsTitle(lhsTheme, lhsText): + if case let .reactionsTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } + case let .reactionsGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType, lhsNoInitialZoom): + if case let .reactionsGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType, rhsNoInitialZoom) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType, lhsNoInitialZoom == rhsNoInitialZoom { return true } else { return false @@ -102,8 +135,8 @@ private enum StatsEntry: ItemListNodeEntry { } else { return false } - case let .publicForward(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsMessage): - if case let .publicForward(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsMessage) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsMessage.id == rhsMessage.id { + case let .publicForward(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPost): + if case let .publicForward(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPost) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsPost == rhsPost { return true } else { return false @@ -120,41 +153,79 @@ private enum StatsEntry: ItemListNodeEntry { switch self { case let .overviewTitle(_, text), let .interactionsTitle(_, text), + let .reactionsTitle(_, text), let .publicForwardsTitle(_, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) - case let .overview(_, stats, publicShares): - return MessageStatsOverviewItem(presentationData: presentationData, stats: stats, publicShares: publicShares, sectionId: self.section, style: .blocks) - case let .interactionsGraph(_, _, _, graph, type): - return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, getDetailsData: { date, completion in + case let .overview(_, stats, storyViews, publicShares): + return StatsOverviewItem(presentationData: presentationData, stats: stats as! Stats, storyViews: storyViews, publicShares: publicShares, sectionId: self.section, style: .blocks) + case let .interactionsGraph(_, _, _, graph, type, noInitialZoom), let .reactionsGraph(_, _, _, graph, type, noInitialZoom): + return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, noInitialZoom: noInitialZoom, getDetailsData: { date, completion in let _ = arguments.loadDetailedGraph(graph, Int64(date.timeIntervalSince1970) * 1000).start(next: { graph in if let graph = graph, case let .Loaded(_, data) = graph { completion(data) } }) }, sectionId: self.section, style: .blocks) - case let .publicForward(_, _, _, _, message): + case let .publicForward(_, _, _, _, item): var views: Int32 = 0 - for attribute in message.attributes { - if let viewsAttribute = attribute as? ViewCountMessageAttribute { - views = Int32(viewsAttribute.count) - break + var forwards: Int32 = 0 + var reactions: Int32 = 0 + + let peer: Peer + switch item { + case let .message(message): + peer = message.peers[message.id.peerId]! + for attribute in message.attributes { + if let viewsAttribute = attribute as? ViewCountMessageAttribute { + views = Int32(viewsAttribute.count) + } else if let forwardsAttribute = attribute as? ForwardCountMessageAttribute { + forwards = Int32(forwardsAttribute.count) + } else if let reactionsAttribute = attribute as? ReactionsMessageAttribute { + reactions = reactionsAttribute.reactions.reduce(0, { partialResult, reaction in + return partialResult + reaction.count + }) + } } + case let .story(peerValue, story): + peer = peerValue._asPeer() + views = Int32(story.views?.seenCount ?? 0) + forwards = Int32(story.views?.forwardCount ?? 0) + reactions = Int32(story.views?.reactedCount ?? 0) } - - let text: String = presentationData.strings.Stats_MessageViews(views) - return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: .firstLast, context: arguments.context, peer: EnginePeer(message.peers[message.id.peerId]!), height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(text, .secondary), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: nil), revealOptions: nil, switchValue: nil, enabled: true, highlighted: false, selectable: true, sectionId: self.section, action: { - arguments.openMessage(message.id) - }, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: nil, contextAction: nil) + return StatsMessageItem(context: arguments.context, presentationData: presentationData, peer: peer, item: item, views: views, reactions: reactions, forwards: forwards, isPeer: true, sectionId: self.section, style: .blocks, action: { + switch item { + case let .message(message): + arguments.openMessage(message.id) + case .story: + break + } + }, openStory: { view in + if case let .story(peer, story) = item { + arguments.openStory(peer.id, story, view) + } + }, contextAction: nil) } } } -private func messageStatsControllerEntries(data: MessageStats?, messages: SearchMessagesResult?, presentationData: PresentationData) -> [StatsEntry] { +private func messageStatsControllerEntries(data: PostStats?, storyViews: EngineStoryItem.Views?, messages: SearchMessagesResult?, forwards: StoryStatsPublicForwardsContext.State?, presentationData: PresentationData) -> [StatsEntry] { var entries: [StatsEntry] = [] if let data = data { entries.append(.overviewTitle(presentationData.theme, presentationData.strings.Stats_MessageOverview.uppercased())) - entries.append(.overview(presentationData.theme, data, messages?.totalCount)) + + var publicShares: Int32? + if let messages { + publicShares = messages.totalCount + } else if let forwards { + publicShares = forwards.count + } + entries.append(.overview(presentationData.theme, data, storyViews, publicShares)) + + var isStories = false + if let _ = data as? StoryStats { + isStories = true + } if !data.interactionsGraph.isEmpty { entries.append(.interactionsTitle(presentationData.theme, presentationData.strings.Stats_MessageInteractionsTitle.uppercased())) @@ -168,14 +239,33 @@ private func messageStatsControllerEntries(data: MessageStats?, messages: Search chartType = .twoAxisStep } - entries.append(.interactionsGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.interactionsGraph, chartType)) + entries.append(.interactionsGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.interactionsGraph, chartType, isStories)) + } + + if !data.reactionsGraph.isEmpty { + entries.append(.reactionsTitle(presentationData.theme, presentationData.strings.Stats_MessageReactionsTitle.uppercased())) + entries.append(.reactionsGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.reactionsGraph, .bars, isStories)) } - if let messages = messages, !messages.messages.isEmpty { + if let messages, !messages.messages.isEmpty { entries.append(.publicForwardsTitle(presentationData.theme, presentationData.strings.Stats_MessagePublicForwardsTitle.uppercased())) var index: Int32 = 0 for message in messages.messages { - entries.append(.publicForward(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, EngineMessage(message))) + entries.append(.publicForward(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, .message(message))) + index += 1 + } + } + + if let forwards, !forwards.forwards.isEmpty { + entries.append(.publicForwardsTitle(presentationData.theme, presentationData.strings.Stats_MessagePublicForwardsTitle.uppercased())) + var index: Int32 = 0 + for forward in forwards.forwards { + switch forward { + case let .message(message): + entries.append(.publicForward(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, .message(message._asMessage()))) + case let .story(peer, story): + entries.append(.publicForward(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, .story(peer, story))) + } index += 1 } } @@ -184,48 +274,142 @@ private func messageStatsControllerEntries(data: MessageStats?, messages: Search return entries } -public func messageStatsController(context: AccountContext, messageId: EngineMessage.Id, statsDatacenterId: Int32?) -> ViewController { +public enum StatsSubject { + case message(id: EngineMessage.Id) + case story(peerId: EnginePeer.Id, id: Int32, item: EngineStoryItem, fromStory: Bool) +} + +protocol PostStats { + var interactionsGraph: StatsGraph { get } + var interactionsGraphDelta: Int64 { get } + var reactionsGraph: StatsGraph { get } +} + +extension MessageStats: PostStats { + +} + +extension StoryStats: PostStats { + +} + +public func messageStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, subject: StatsSubject) -> ViewController { var navigateToMessageImpl: ((EngineMessage.Id) -> Void)? let actionsDisposable = DisposableSet() - let dataPromise = Promise(nil) + let dataPromise = Promise(nil) let messagesPromise = Promise<(SearchMessagesResult, SearchMessagesState)?>(nil) + let forwardsPromise = Promise(nil) + + let anyStatsContext: Any + let dataSignal: Signal + var loadDetailedGraphImpl: ((StatsGraph, Int64) -> Signal)? + var openStoryImpl: ((EnginePeer.Id, EngineStoryItem, UIView) -> Void)? - let datacenterId: Int32 = statsDatacenterId ?? 0 + var forwardsContext: StoryStatsPublicForwardsContext? + let peerId: EnginePeer.Id + var storyItem: EngineStoryItem? + switch subject { + case let .message(id): + peerId = id.peerId + let statsContext = MessageStatsContext(account: context.account, messageId: id) + loadDetailedGraphImpl = { [weak statsContext] graph, x in + return statsContext?.loadDetailedGraph(graph, x: x) ?? .single(nil) + } + dataSignal = statsContext.state + |> map { state in + return state.stats + } + dataPromise.set(.single(nil) |> then(dataSignal)) + anyStatsContext = statsContext + + let searchSignal = context.engine.messages.searchMessages(location: .publicForwards(messageId: id), query: "", state: nil) + |> map(Optional.init) + |> afterNext { result in + if let result = result { + for message in result.0.messages { + if let peer = message.peers[message.id.peerId], let peerReference = PeerReference(peer) { + let _ = context.engine.peers.updatedRemotePeer(peer: peerReference).start() + } + } + } + } + messagesPromise.set(.single(nil) |> then(searchSignal)) + forwardsPromise.set(.single(nil)) + case let .story(peerIdValue, id, item, _): + peerId = peerIdValue + storyItem = item + + let statsContext = StoryStatsContext(account: context.account, peerId: peerId, storyId: id) + loadDetailedGraphImpl = { [weak statsContext] graph, x in + return statsContext?.loadDetailedGraph(graph, x: x) ?? .single(nil) + } + dataSignal = statsContext.state + |> map { state in + return state.stats + } + dataPromise.set(.single(nil) |> then(dataSignal)) + anyStatsContext = statsContext + + messagesPromise.set(.single(nil)) - let statsContext = MessageStatsContext(postbox: context.account.postbox, network: context.account.network, datacenterId: datacenterId, messageId: messageId) - let dataSignal: Signal = statsContext.state - |> map { state in - return state.stats + forwardsContext = StoryStatsPublicForwardsContext(account: context.account, peerId: peerId, storyId: id) + if let forwardsContext { + forwardsPromise.set( + .single(nil) + |> then( + forwardsContext.state + |> map(Optional.init) + ) + ) + } else { + forwardsPromise.set(.single(nil)) + } } - dataPromise.set(.single(nil) |> then(dataSignal)) let arguments = MessageStatsControllerArguments(context: context, loadDetailedGraph: { graph, x -> Signal in - return statsContext.loadDetailedGraph(graph, x: x) + return loadDetailedGraphImpl?(graph, x) ?? .single(nil) }, openMessage: { messageId in navigateToMessageImpl?(messageId) + }, openStory: { peerId, story, view in + openStoryImpl?(peerId, story, view) }) let longLoadingSignal: Signal = .single(false) |> then(.single(true) |> delay(2.0, queue: Queue.mainQueue())) - let previousData = Atomic(value: nil) + let previousData = Atomic(value: nil) - let searchSignal = context.engine.messages.searchMessages(location: .publicForwards(messageId: messageId, datacenterId: Int(datacenterId)), query: "", state: nil) - |> map(Optional.init) - |> afterNext { result in - if let result = result { - for message in result.0.messages { - if let peer = message.peers[message.id.peerId], let peerReference = PeerReference(peer) { - let _ = context.engine.peers.updatedRemotePeer(peer: peerReference).start() + let iconNodePromise = Promise() + if case let .story(peerId, id, storyItem, fromStory) = subject, !fromStory { + let _ = id + iconNodePromise.set( + context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) + |> deliverOnMainQueue + |> map { peer -> ASDisplayNode? in + if let peer = peer?._asPeer() { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + return StoryIconNode(context: context, theme: presentationData.theme, peer: peer, storyItem: storyItem) + } else { + return nil } } - } + ) + + } else { + iconNodePromise.set(.single(nil)) } - messagesPromise.set(.single(nil) |> then(searchSignal)) - let signal = combineLatest(context.sharedContext.presentationData, dataPromise.get(), messagesPromise.get(), longLoadingSignal) + let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData + let signal = combineLatest( + presentationData, + dataPromise.get(), + messagesPromise.get(), + forwardsPromise.get(), + longLoadingSignal, + iconNodePromise.get() + ) |> deliverOnMainQueue - |> map { presentationData, data, search, longLoading -> (ItemListControllerState, (ItemListNodeState, Any)) in + |> map { presentationData, data, search, forwards, longLoading, iconNode -> (ItemListControllerState, (ItemListNodeState, Any)) in let previous = previousData.swap(data) var emptyStateItem: ItemListControllerEmptyStateItem? if data == nil { @@ -236,14 +420,29 @@ public func messageStatsController(context: AccountContext, messageId: EngineMes } } - let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.Stats_MessageTitle), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) - let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: messageStatsControllerEntries(data: data, messages: search?.0, presentationData: presentationData), style: .blocks, emptyStateItem: emptyStateItem, crossfadeState: previous == nil, animateChanges: false) + let title: String + var storyViews: EngineStoryItem.Views? + switch subject { + case .message: + title = presentationData.strings.Stats_MessageTitle + case let .story(_, _, storyItem, _): + title = presentationData.strings.Stats_StoryTitle + storyViews = storyItem.views + } + + let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(title), leftNavigationButton: nil, rightNavigationButton: iconNode.flatMap { ItemListNavigationButton(content: .node($0), style: .regular, enabled: true, action: { [weak iconNode] in + if let iconNode, let storyItem { + openStoryImpl?(peerId, storyItem, iconNode.view) + } + }) }, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) + let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: messageStatsControllerEntries(data: data, storyViews: storyViews, messages: search?.0, forwards: forwards, presentationData: presentationData), style: .blocks, emptyStateItem: emptyStateItem, crossfadeState: previous == nil, animateChanges: false) return (controllerState, (listState, arguments)) } |> afterDisposed { actionsDisposable.dispose() - let _ = statsContext.state + let _ = anyStatsContext + let _ = forwardsContext } let controller = ItemListController(context: context, state: signal) @@ -254,6 +453,11 @@ public func messageStatsController(context: AccountContext, messageId: EngineMes } }) } + controller.visibleBottomContentOffsetChanged = { [weak forwardsContext] offset in + if case let .known(value) = offset, value < 100.0, case .story = subject { + forwardsContext?.loadMore() + } + } controller.didDisappear = { [weak controller] _ in controller?.clearItemNodesHighlight(animated: true) } @@ -270,5 +474,65 @@ public func messageStatsController(context: AccountContext, messageId: EngineMes } }) } + openStoryImpl = { [weak controller] peerId, story, sourceView in + let storyContent = SingleStoryContentContextImpl(context: context, storyId: StoryId(peerId: peerId, id: story.id), storyItem: story, readGlobally: false) + let _ = (storyContent.state + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { [weak controller, weak sourceView] _ in + guard let controller, let sourceView else { + return + } + let transitionIn = StoryContainerScreen.TransitionIn( + sourceView: sourceView, + sourceRect: sourceView.bounds, + sourceCornerRadius: sourceView.bounds.width * 0.5, + sourceIsAvatar: false + ) + + let storyContainerScreen = StoryContainerScreen( + context: context, + content: storyContent, + transitionIn: transitionIn, + transitionOut: { [weak sourceView] peerId, storyIdValue in + if let sourceView { + let destinationView = sourceView + return StoryContainerScreen.TransitionOut( + destinationView: destinationView, + transitionView: StoryContainerScreen.TransitionView( + makeView: { [weak destinationView] in + let parentView = UIView() + if let copyView = destinationView?.snapshotContentTree(unhide: true) { + parentView.addSubview(copyView) + } + return parentView + }, + updateView: { copyView, state, transition in + guard let view = copyView.subviews.first else { + return + } + let size = state.sourceSize.interpolate(to: state.destinationSize, amount: state.progress) + transition.setPosition(view: view, position: CGPoint(x: size.width * 0.5, y: size.height * 0.5)) + transition.setScale(view: view, scale: size.width / state.destinationSize.width) + }, + insertCloneTransitionView: nil + ), + destinationRect: destinationView.bounds, + destinationCornerRadius: destinationView.bounds.width * 0.5, + destinationIsAvatar: false, + completed: { [weak sourceView] in + guard let sourceView else { + return + } + sourceView.isHidden = false + } + ) + } else { + return nil + } + } + ) + controller.push(storyContainerScreen) + }) + } return controller } diff --git a/submodules/StatisticsUI/Sources/MessageStatsOverviewItem.swift b/submodules/StatisticsUI/Sources/MessageStatsOverviewItem.swift deleted file mode 100644 index 22a0549e825..00000000000 --- a/submodules/StatisticsUI/Sources/MessageStatsOverviewItem.swift +++ /dev/null @@ -1,303 +0,0 @@ -import Foundation -import UIKit -import Display -import AsyncDisplayKit -import SwiftSignalKit -import TelegramCore -import TelegramPresentationData -import ItemListUI -import PresentationDataUtils - -class MessageStatsOverviewItem: ListViewItem, ItemListItem { - let presentationData: ItemListPresentationData - let stats: MessageStats - let publicShares: Int32? - let sectionId: ItemListSectionId - let style: ItemListStyle - - init(presentationData: ItemListPresentationData, stats: MessageStats, publicShares: Int32?, sectionId: ItemListSectionId, style: ItemListStyle) { - self.presentationData = presentationData - self.stats = stats - self.publicShares = publicShares - self.sectionId = sectionId - self.style = style - } - - func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { - async { - let node = MessageStatsOverviewItemNode() - let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) - - node.contentSize = layout.contentSize - node.insets = layout.insets - - Queue.mainQueue().async { - completion(node, { - return (nil, { _ in apply() }) - }) - } - } - } - - 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? MessageStatsOverviewItemNode { - let makeLayout = nodeValue.asyncLayout() - - async { - let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) - Queue.mainQueue().async { - completion(layout, { _ in - apply() - }) - } - } - } - } - } - - var selectable: Bool = false -} - -class MessageStatsOverviewItemNode: ListViewItemNode { - private let backgroundNode: ASDisplayNode - private let topStripeNode: ASDisplayNode - private let bottomStripeNode: ASDisplayNode - private let maskNode: ASImageNode - - private let leftValueLabel: ImmediateTextNode - private let centerValueLabel: ImmediateTextNode - private let rightValueLabel: ImmediateTextNode - - private let leftTitleLabel: ImmediateTextNode - private let centerTitleLabel: ImmediateTextNode - private let rightTitleLabel: ImmediateTextNode - - private var item: MessageStatsOverviewItem? - - init() { - self.backgroundNode = ASDisplayNode() - self.backgroundNode.isLayerBacked = true - self.backgroundNode.backgroundColor = .white - - self.topStripeNode = ASDisplayNode() - self.topStripeNode.isLayerBacked = true - - self.bottomStripeNode = ASDisplayNode() - self.bottomStripeNode.isLayerBacked = true - - self.maskNode = ASImageNode() - - self.leftValueLabel = ImmediateTextNode() - self.centerValueLabel = ImmediateTextNode() - self.rightValueLabel = ImmediateTextNode() - - self.leftTitleLabel = ImmediateTextNode() - self.centerTitleLabel = ImmediateTextNode() - self.rightTitleLabel = ImmediateTextNode() - - super.init(layerBacked: false, dynamicBounce: false) - - self.clipsToBounds = true - - self.addSubnode(self.leftValueLabel) - self.addSubnode(self.centerValueLabel) - self.addSubnode(self.rightValueLabel) - - self.addSubnode(self.leftTitleLabel) - self.addSubnode(self.centerTitleLabel) - self.addSubnode(self.rightTitleLabel) - } - - func asyncLayout() -> (_ item: MessageStatsOverviewItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { - let makeLeftValueLabelLayout = TextNode.asyncLayout(self.leftValueLabel) - let makeRightValueLabelLayout = TextNode.asyncLayout(self.rightValueLabel) - let makeCenterValueLabelLayout = TextNode.asyncLayout(self.centerValueLabel) - - let makeLeftTitleLabelLayout = TextNode.asyncLayout(self.leftTitleLabel) - let makeRightTitleLabelLayout = TextNode.asyncLayout(self.rightTitleLabel) - let makeCenterTitleLabelLayout = TextNode.asyncLayout(self.centerTitleLabel) - - let currentItem = self.item - - return { item, params, neighbors in - let insets: UIEdgeInsets - let separatorHeight = UIScreenPixel - let itemBackgroundColor: UIColor - let itemSeparatorColor: UIColor - - let topInset: CGFloat = 14.0 - let sideInset: CGFloat = 16.0 - - var height: CGFloat = topInset * 2.0 - - let leftInset = params.leftInset - let rightInset = params.rightInset - var updatedTheme: PresentationTheme? - - if currentItem?.presentationData.theme !== item.presentationData.theme { - updatedTheme = item.presentationData.theme - } - - switch item.style { - case .plain: - itemBackgroundColor = item.presentationData.theme.list.plainBackgroundColor - itemSeparatorColor = item.presentationData.theme.list.itemPlainSeparatorColor - insets = itemListNeighborsPlainInsets(neighbors) - case .blocks: - itemBackgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor - itemSeparatorColor = item.presentationData.theme.list.itemBlocksSeparatorColor - insets = itemListNeighborsGroupedInsets(neighbors, params) - } - - let valueFont = Font.semibold(item.presentationData.fontSize.itemListBaseFontSize) - let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseHeaderFontSize) - - let leftValueLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))? - let rightValueLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))? - let centerValueLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))? - - let leftTitleLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))? - let rightTitleLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))? - let centerTitleLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))? - - leftValueLabelLayoutAndApply = makeLeftValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: compactNumericCountString(item.stats.views), font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - centerValueLabelLayoutAndApply = makeCenterValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.publicShares.flatMap { compactNumericCountString(Int($0)) } ?? "–", font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - rightValueLabelLayoutAndApply = makeRightValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.publicShares.flatMap { "≈\( compactNumericCountString(max(0, item.stats.forwards - Int($0))))" } ?? "–", font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - var remainingWidth: CGFloat = params.width - leftInset - rightInset - sideInset * 2.0 - let maxItemWidth: CGFloat = floor(remainingWidth / 2.8) - - leftTitleLabelLayoutAndApply = makeLeftTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_Message_Views, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: min(maxItemWidth, remainingWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - remainingWidth -= leftTitleLabelLayoutAndApply!.0.size.width - 4.0 - - centerTitleLabelLayoutAndApply = makeCenterTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_Message_PublicShares, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: min(maxItemWidth, remainingWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - remainingWidth -= centerTitleLabelLayoutAndApply!.0.size.width - 4.0 - - rightTitleLabelLayoutAndApply = makeRightTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_Message_PrivateShares, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: min(maxItemWidth, remainingWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - var maxLabelHeight = rightTitleLabelLayoutAndApply!.0.size.height - maxLabelHeight = max(maxLabelHeight, centerTitleLabelLayoutAndApply!.0.size.height) - maxLabelHeight = max(maxLabelHeight, leftTitleLabelLayoutAndApply!.0.size.height) - - height += rightValueLabelLayoutAndApply!.0.size.height + maxLabelHeight - - let contentSize = CGSize(width: params.width, height: height) - return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] in - if let strongSelf = self { - strongSelf.item = item - - let _ = leftValueLabelLayoutAndApply?.1() - let _ = centerValueLabelLayoutAndApply?.1() - let _ = rightValueLabelLayoutAndApply?.1() - let _ = leftTitleLabelLayoutAndApply?.1() - let _ = centerTitleLabelLayoutAndApply?.1() - let _ = rightTitleLabelLayoutAndApply?.1() - - if let _ = updatedTheme { - strongSelf.topStripeNode.backgroundColor = itemSeparatorColor - strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor - strongSelf.backgroundNode.backgroundColor = itemBackgroundColor - } - - switch item.style { - case .plain: - if strongSelf.backgroundNode.supernode != nil { - strongSelf.backgroundNode.removeFromSupernode() - } - if strongSelf.topStripeNode.supernode != nil { - strongSelf.topStripeNode.removeFromSupernode() - } - if strongSelf.bottomStripeNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 0) - } - if strongSelf.maskNode.supernode != nil { - strongSelf.maskNode.removeFromSupernode() - } - strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight)) - case .blocks: - if strongSelf.backgroundNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0) - } - if strongSelf.topStripeNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1) - } - if strongSelf.bottomStripeNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) - } - if strongSelf.maskNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.maskNode, at: 3) - } - - let hasCorners = itemListHasRoundedBlockLayout(params) - var hasTopCorners = false - var hasBottomCorners = false - switch neighbors.top { - case .sameSection(false): - strongSelf.topStripeNode.isHidden = true - default: - hasTopCorners = true - strongSelf.topStripeNode.isHidden = hasCorners - } - let bottomStripeInset: CGFloat - switch neighbors.bottom { - case .sameSection(false): - bottomStripeInset = leftInset - strongSelf.bottomStripeNode.isHidden = false - default: - bottomStripeInset = 0.0 - hasBottomCorners = true - strongSelf.bottomStripeNode.isHidden = hasCorners - } - - strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil - - strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) - strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) - strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: separatorHeight)) - strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight)) - } - - let maxLeftWidth = max(leftValueLabelLayoutAndApply?.0.size.width ?? 0.0, leftTitleLabelLayoutAndApply?.0.size.width ?? 0.0) - let maxCenterWidth = max(centerValueLabelLayoutAndApply?.0.size.width ?? 0.0, centerTitleLabelLayoutAndApply?.0.size.width ?? 0.0) - let maxRightWidth = max(rightValueLabelLayoutAndApply?.0.size.width ?? 0.0, rightTitleLabelLayoutAndApply?.0.size.width ?? 0.0) - - let horizontalSpacing = max(1.0, min(60, (params.width - leftInset - rightInset - sideInset * 2.0 - maxLeftWidth - maxCenterWidth - maxRightWidth) / 2.0)) - - var x: CGFloat = leftInset + (params.width - leftInset - rightInset - maxLeftWidth - maxCenterWidth - maxRightWidth - horizontalSpacing * 2.0) / 2.0 - if let leftValueLabelLayout = leftValueLabelLayoutAndApply?.0, let leftTitleLabelLayout = leftTitleLabelLayoutAndApply?.0 { - strongSelf.leftValueLabel.frame = CGRect(origin: CGPoint(x: x, y: topInset), size: leftValueLabelLayout.size) - strongSelf.leftTitleLabel.frame = CGRect(origin: CGPoint(x: x, y: strongSelf.leftValueLabel.frame.maxY), size: leftTitleLabelLayout.size) - x += max(leftValueLabelLayout.size.width, leftTitleLabelLayout.size.width) + horizontalSpacing - } - - if let centerValueLabelLayout = centerValueLabelLayoutAndApply?.0, let centerTitleLabelLayout = centerTitleLabelLayoutAndApply?.0 { - strongSelf.centerValueLabel.frame = CGRect(origin: CGPoint(x: x, y: topInset), size: centerValueLabelLayout.size) - strongSelf.centerTitleLabel.frame = CGRect(origin: CGPoint(x: x, y: strongSelf.centerValueLabel.frame.maxY), size: centerTitleLabelLayout.size) - x += max(centerValueLabelLayout.size.width, centerTitleLabelLayout.size.width) + horizontalSpacing - } - - if let rightValueLabelLayout = rightValueLabelLayoutAndApply?.0, let rightTitleLabelLayout = rightTitleLabelLayoutAndApply?.0 { - strongSelf.rightValueLabel.frame = CGRect(origin: CGPoint(x: x, y: topInset), size: rightValueLabelLayout.size) - strongSelf.rightTitleLabel.frame = CGRect(origin: CGPoint(x: x, y: strongSelf.rightValueLabel.frame.maxY), size: rightTitleLabelLayout.size) - } - } - }) - } - } - - override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { - self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) - } - - override func animateAdded(_ currentTimestamp: Double, duration: Double) { - self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - } - - override func animateRemoved(_ currentTimestamp: Double, duration: Double) { - self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) - } -} - diff --git a/submodules/StatisticsUI/Sources/StatsGraphItem.swift b/submodules/StatisticsUI/Sources/StatsGraphItem.swift index dd10d55497e..c0abb3021d0 100644 --- a/submodules/StatisticsUI/Sources/StatsGraphItem.swift +++ b/submodules/StatisticsUI/Sources/StatsGraphItem.swift @@ -15,14 +15,16 @@ class StatsGraphItem: ListViewItem, ItemListItem { let presentationData: ItemListPresentationData let graph: StatsGraph let type: ChartType + let noInitialZoom: Bool let getDetailsData: ((Date, @escaping (String?) -> Void) -> Void)? let sectionId: ItemListSectionId let style: ItemListStyle - init(presentationData: ItemListPresentationData, graph: StatsGraph, type: ChartType, getDetailsData: ((Date, @escaping (String?) -> Void) -> Void)? = nil, sectionId: ItemListSectionId, style: ItemListStyle) { + init(presentationData: ItemListPresentationData, graph: StatsGraph, type: ChartType, noInitialZoom: Bool = false, getDetailsData: ((Date, @escaping (String?) -> Void) -> Void)? = nil, sectionId: ItemListSectionId, style: ItemListStyle) { self.presentationData = presentationData self.graph = graph self.type = type + self.noInitialZoom = noInitialZoom self.getDetailsData = getDetailsData self.sectionId = sectionId self.style = style @@ -180,6 +182,7 @@ class StatsGraphItemNode: ListViewItemNode { if let visibilityHeight = visibilityHeight { contentSize.height += visibilityHeight } + contentSize.height += 7.0 let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] in @@ -265,7 +268,7 @@ class StatsGraphItemNode: ListViewItemNode { if let updatedGraph = updatedGraph { if case .Loaded = updatedGraph, let updatedController = updatedController { - strongSelf.chartNode.setup(controller: updatedController) + strongSelf.chartNode.setup(controller: updatedController, noInitialZoom: item.noInitialZoom) strongSelf.activityIndicator.isHidden = true strongSelf.chartNode.isHidden = false } else if case .OnDemand = updatedGraph { diff --git a/submodules/StatisticsUI/Sources/StatsMessageItem.swift b/submodules/StatisticsUI/Sources/StatsMessageItem.swift index 4987f304466..4691740299d 100644 --- a/submodules/StatisticsUI/Sources/StatsMessageItem.swift +++ b/submodules/StatisticsUI/Sources/StatsMessageItem.swift @@ -2,6 +2,7 @@ import Foundation import UIKit import Display import AsyncDisplayKit +import ComponentFlow import SwiftSignalKit import Postbox import TelegramCore @@ -11,27 +12,37 @@ import TelegramStringFormatting import ItemListUI import PresentationDataUtils import PhotoResources +import AvatarStoryIndicatorComponent +import AvatarNode public class StatsMessageItem: ListViewItem, ItemListItem { let context: AccountContext let presentationData: ItemListPresentationData - let message: Message + let peer: Peer + let item: StatsPostItem let views: Int32 + let reactions: Int32 let forwards: Int32 + let isPeer: Bool public let sectionId: ItemListSectionId let style: ItemListStyle let action: (() -> Void)? + let openStory: (UIView) -> Void let contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? - init(context: AccountContext, presentationData: ItemListPresentationData, message: Message, views: Int32, forwards: Int32, sectionId: ItemListSectionId, style: ItemListStyle, action: (() -> Void)?, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?) { + init(context: AccountContext, presentationData: ItemListPresentationData, peer: Peer, item: StatsPostItem, views: Int32, reactions: Int32, forwards: Int32, isPeer: Bool = false, sectionId: ItemListSectionId, style: ItemListStyle, action: (() -> Void)?, openStory: @escaping (UIView) -> Void, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?) { self.context = context self.presentationData = presentationData - self.message = message + self.peer = peer + self.item = item self.views = views + self.reactions = reactions self.forwards = forwards + self.isPeer = isPeer self.sectionId = sectionId self.style = style self.action = action + self.openStory = openStory self.contextAction = contextAction } @@ -72,14 +83,13 @@ public class StatsMessageItem: ListViewItem, ItemListItem { public func selected(listView: ListView){ listView.clearHighlightAnimated(true) - self.action?() } } private let badgeFont = Font.regular(15.0) -public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { +final class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { private let backgroundNode: ASDisplayNode private let topStripeNode: ASDisplayNode private let bottomStripeNode: ASDisplayNode @@ -95,10 +105,18 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { private var extractedRect: CGRect? private var nonExtractedRect: CGRect? + var avatarNode: AvatarNode? let contentImageNode: TransformImageNode + var storyIndicator: ComponentView? + var storyButton: HighlightTrackingButton? + let titleNode: TextNode let labelNode: TextNode let viewsNode: TextNode + + let reactionsIconNode: ASImageNode + let reactionsNode: TextNode + let forwardsIconNode: ASImageNode let forwardsNode: TextNode private let activateArea: AccessibilityAreaNode @@ -135,7 +153,7 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { self.extractedBackgroundImageNode.alpha = 0.0 self.contentImageNode = TransformImageNode() - self.contentImageNode.isLayerBacked = true + self.contentImageNode.isLayerBacked = false self.offsetContainerNode = ASDisplayNode() self.countersContainerNode = ASDisplayNode() @@ -152,6 +170,15 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { self.forwardsNode = TextNode() self.forwardsNode.isUserInteractionEnabled = false + self.forwardsIconNode = ASImageNode() + self.forwardsIconNode.displaysAsynchronously = false + + self.reactionsNode = TextNode() + self.reactionsNode.isUserInteractionEnabled = false + + self.reactionsIconNode = ASImageNode() + self.reactionsIconNode.displaysAsynchronously = false + self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true @@ -172,6 +199,9 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { self.offsetContainerNode.addSubnode(self.labelNode) self.countersContainerNode.addSubnode(self.viewsNode) self.countersContainerNode.addSubnode(self.forwardsNode) + self.countersContainerNode.addSubnode(self.forwardsIconNode) + self.countersContainerNode.addSubnode(self.reactionsNode) + self.countersContainerNode.addSubnode(self.reactionsIconNode) self.containerNode.targetNodeForActivationProgress = self.contextSourceNode.contentNode self.addSubnode(self.activateArea) @@ -200,8 +230,8 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { transition.updateAlpha(node: strongSelf.countersContainerNode, alpha: isExtracted ? 0.0 : 1.0) - transition.updateSublayerTransformOffset(layer: strongSelf.countersContainerNode.layer, offset: CGPoint(x: isExtracted ? -24.0 : 0.0, y: 0.0)) - transition.updateSublayerTransformOffset(layer: strongSelf.offsetContainerNode.layer, offset: CGPoint(x: isExtracted ? 12.0 : 0.0, y: 0.0)) + transition.updateSublayerTransformOffset(layer: strongSelf.countersContainerNode.layer, offset: CGPoint(x: isExtracted ? -16.0 : 0.0, y: 0.0)) + transition.updateSublayerTransformOffset(layer: strongSelf.offsetContainerNode.layer, offset: CGPoint(x: isExtracted ? 16.0 : 0.0, y: 0.0)) transition.updateAlpha(node: strongSelf.extractedBackgroundImageNode, alpha: isExtracted ? 1.0 : 0.0, completion: { _ in if !isExtracted { @@ -211,10 +241,42 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { } } + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + let result = super.hitTest(point, with: event) + return result + } + + override func selected() { + guard let item = self.item else { + return + } + if item.isPeer { + if case .story = item.item { + self.storyPressed() + } else { + item.action?() + } + } else { + item.action?() + } + } + + @objc private func storyPressed() { + guard let item = self.item else { + return + } + if let avatarNode = self.avatarNode { + item.openStory(avatarNode.view) + } else { + item.openStory(self.contentImageNode.view) + } + } + public func asyncLayout() -> (_ item: StatsMessageItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { let makeTitleLayout = TextNode.asyncLayout(self.titleNode) let makeLabelLayout = TextNode.asyncLayout(self.labelNode) let makeViewsLayout = TextNode.asyncLayout(self.viewsNode) + let makeReactionsLayout = TextNode.asyncLayout(self.reactionsNode) let makeForwardsLayout = TextNode.asyncLayout(self.forwardsNode) let currentItem = self.item @@ -236,67 +298,104 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { let leftInset = 16.0 + params.leftInset let rightInset = 16.0 + params.rightInset var totalLeftInset = leftInset - let additionalRightInset: CGFloat = 128.0 - let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseFontSize) + let titleFont = Font.semibold(item.presentationData.fontSize.itemListBaseFontSize) + let labelFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 13.0 / 17.0)) let presentationData = item.context.sharedContext.currentPresentationData.with { $0 } - let contentKind = messageContentKind(contentSettings: item.context.currentContentSettings.with { $0 }, message: EngineMessage(item.message), strings: item.presentationData.strings, nameDisplayOrder: .firstLast, dateTimeFormat: presentationData.dateTimeFormat, accountPeerId: item.context.account.peerId) - var text = !item.message.text.isEmpty ? item.message.text : stringForMediaKind(contentKind, strings: item.presentationData.strings).0.string - text = foldLineBreaks(text) + var text: String var contentImageMedia: Media? - for media in item.message.media { - if let image = media as? TelegramMediaImage { - contentImageMedia = image - break - } else if let file = media as? TelegramMediaFile { - if file.isVideo && !file.isInstantVideo { - contentImageMedia = file - break - } - } else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content { - if let image = content.image { + let timestamp: Int32 + + switch item.item { + case let .message(message): + let contentKind: MessageContentKind + contentKind = messageContentKind(contentSettings: item.context.currentContentSettings.with { $0 }, message: EngineMessage(message), strings: item.presentationData.strings, nameDisplayOrder: .firstLast, dateTimeFormat: presentationData.dateTimeFormat, accountPeerId: item.context.account.peerId) + text = !message.text.isEmpty ? message.text : stringForMediaKind(contentKind, strings: item.presentationData.strings).0.string + + for media in message.media { + if let image = media as? TelegramMediaImage { contentImageMedia = image break - } else if let file = content.file { + } else if let file = media as? TelegramMediaFile { if file.isVideo && !file.isInstantVideo { contentImageMedia = file break } + } else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content { + if let image = content.image { + contentImageMedia = image + break + } else if let file = content.file { + if file.isVideo && !file.isInstantVideo { + contentImageMedia = file + break + } + } } } + timestamp = message.timestamp + case let .story(_, story): + text = item.presentationData.strings.Message_Story + timestamp = story.timestamp + if let image = story.media._asMedia() as? TelegramMediaImage { + contentImageMedia = image + break + } else if let file = story.media._asMedia() as? TelegramMediaFile { + contentImageMedia = file + break + } + } + + if item.isPeer { + text = EnginePeer(item.peer).displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) + } else { + text = foldLineBreaks(text) } - if let _ = contentImageMedia { - totalLeftInset += 48.0 + if contentImageMedia != nil || item.isPeer { + totalLeftInset += 46.0 } var updateImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? if let contentImageMedia = contentImageMedia { if let currentContentImageMedia = currentContentImageMedia, contentImageMedia.isSemanticallyEqual(to: currentContentImageMedia) { } else { - if let image = contentImageMedia as? TelegramMediaImage { - updateImageSignal = mediaGridMessagePhoto(account: item.context.account, userLocation: .peer(item.message.id.peerId), photoReference: .message(message: MessageReference(item.message), media: image)) - } else if let file = contentImageMedia as? TelegramMediaFile { - updateImageSignal = mediaGridMessageVideo(postbox: item.context.account.postbox, userLocation: .peer(item.message.id.peerId), videoReference: .message(message: MessageReference(item.message), media: file), autoFetchFullSizeThumbnail: true) + switch item.item { + case let .message(message): + if let image = contentImageMedia as? TelegramMediaImage { + updateImageSignal = mediaGridMessagePhoto(account: item.context.account, userLocation: .peer(message.id.peerId), photoReference: .message(message: MessageReference(message), media: image)) + } else if let file = contentImageMedia as? TelegramMediaFile { + updateImageSignal = mediaGridMessageVideo(postbox: item.context.account.postbox, userLocation: .peer(message.id.peerId), videoReference: .message(message: MessageReference(message), media: file), autoFetchFullSizeThumbnail: true) + } + case let .story(_, story): + if let peerReference = PeerReference(item.peer) { + if let image = contentImageMedia as? TelegramMediaImage { + updateImageSignal = mediaGridMessagePhoto(account: item.context.account, userLocation: .peer(item.peer.id), photoReference: .story(peer: peerReference, id: story.id, media: image)) + } else if let file = contentImageMedia as? TelegramMediaFile { + updateImageSignal = mediaGridMessageVideo(postbox: item.context.account.postbox, userLocation: .peer(item.peer.id), videoReference: .story(peer: peerReference, id: story.id, media: file), autoFetchFullSizeThumbnail: true) + } + } } } } - let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: text, font: titleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - totalLeftInset - rightInset - additionalRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - let labelFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 13.0 / 17.0)) + let (viewsLayout, viewsApply) = makeViewsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_MessageViews(item.views), font: labelFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 128.0, height: CGFloat.greatestFiniteMagnitude), alignment: .right, cutout: nil, insets: UIEdgeInsets())) - let label = stringForMediumDate(timestamp: item.message.timestamp, strings: item.presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat) + let reactions = item.reactions > 0 ? compactNumericCountString(Int(item.reactions), decimalSeparator: item.presentationData.dateTimeFormat.decimalSeparator) : "" + let (reactionsLayout, reactionsApply) = makeReactionsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: reactions, font: labelFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 128.0, height: CGFloat.greatestFiniteMagnitude), alignment: .right, cutout: nil, insets: UIEdgeInsets())) - let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: label, font: labelFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - totalLeftInset - rightInset - additionalRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + let forwards = item.forwards > 0 ? compactNumericCountString(Int(item.forwards), decimalSeparator: item.presentationData.dateTimeFormat.decimalSeparator) : "" + let (forwardsLayout, forwardsApply) = makeForwardsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: forwards, font: labelFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 128.0, height: CGFloat.greatestFiniteMagnitude), alignment: .right, cutout: nil, insets: UIEdgeInsets())) - let (viewsLayout, viewsApply) = makeViewsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_MessageViews(item.views), font: labelFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 128.0, height: CGFloat.greatestFiniteMagnitude), alignment: .right, cutout: nil, insets: UIEdgeInsets())) - - let (forwardsLayout, forwardsApply) = makeForwardsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_MessageForwards(item.forwards), font: labelFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 128.0, height: CGFloat.greatestFiniteMagnitude), alignment: .right, cutout: nil, insets: UIEdgeInsets())) + let additionalRightInset = max(viewsLayout.size.width, reactionsLayout.size.width + forwardsLayout.size.width + 36.0) + 8.0 + let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: text, font: titleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - totalLeftInset - rightInset - additionalRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + + let label = stringForMediumDate(timestamp: timestamp, strings: item.presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat) + let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: label, font: labelFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - totalLeftInset - rightInset - additionalRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - let verticalInset: CGFloat = 11.0 + let verticalInset: CGFloat = 10.0 let titleSpacing: CGFloat = 3.0 let height: CGFloat = verticalInset * 2.0 + titleLayout.size.height + titleSpacing + labelLayout.size.height @@ -318,8 +417,14 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] in if let strongSelf = self { + let themeUpdated = strongSelf.item?.presentationData.theme !== item.presentationData.theme strongSelf.item = item + if themeUpdated { + strongSelf.forwardsIconNode.image = PresentationResourcesItemList.statsForwardsIcon(item.presentationData.theme) + strongSelf.reactionsIconNode.image = PresentationResourcesItemList.statsReactionsIcon(item.presentationData.theme) + } + let nonExtractedRect = CGRect(origin: CGPoint(), size: CGSize(width: layout.contentSize.width - 16.0, height: layout.contentSize.height)) let extractedRect = CGRect(origin: CGPoint(), size: layout.contentSize).insetBy(dx: 16.0 + params.leftInset, dy: 0.0) strongSelf.extractedRect = extractedRect @@ -350,20 +455,49 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor } + var contentImageSize = CGSize(width: 40.0, height: 40.0) + + var contentImageInset = leftInset - 6.0 var dimensions: CGSize? - if let contentImageMedia = contentImageMedia as? TelegramMediaImage { - dimensions = largestRepresentationForPhoto(contentImageMedia)?.dimensions.cgSize - } else if let contentImageMedia = contentImageMedia as? TelegramMediaFile { - dimensions = contentImageMedia.dimensions?.cgSize + if item.isPeer { + let avatarNode: AvatarNode + if let current = strongSelf.avatarNode { + avatarNode = current + } else { + avatarNode = AvatarNode(font: avatarPlaceholderFont(size: floor(40.0 * 16.0 / 37.0))) + strongSelf.offsetContainerNode.addSubnode(avatarNode) + strongSelf.avatarNode = avatarNode + } + avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: EnginePeer(item.peer)) + + if case .story = item.item { + contentImageInset += 3.0 + contentImageSize = CGSize(width: 34.0, height: 34.0) + } + } else { + strongSelf.avatarNode?.removeFromSupernode() + strongSelf.avatarNode = nil + + if let contentImageMedia = contentImageMedia as? TelegramMediaImage { + dimensions = largestRepresentationForPhoto(contentImageMedia)?.dimensions.cgSize + } else if let contentImageMedia = contentImageMedia as? TelegramMediaFile { + dimensions = contentImageMedia.dimensions?.cgSize + } } - - let contentImageSize = CGSize(width: 40.0, height: 40.0) - + if let dimensions = dimensions { let makeImageLayout = strongSelf.contentImageNode.asyncLayout() - let imageSize = contentImageSize - let applyImageLayout = makeImageLayout(TransformImageArguments(corners: ImageCorners(radius: 4.0), imageSize: dimensions.aspectFilled(imageSize), boundingSize: imageSize, intrinsicInsets: UIEdgeInsets())) + let cornerRadius: CGFloat + if case .story = item.item { + contentImageInset += 3.0 + contentImageSize = CGSize(width: 34.0, height: 34.0) + cornerRadius = contentImageSize.width / 2.0 + } else { + cornerRadius = 6.0 + } + + let applyImageLayout = makeImageLayout(TransformImageArguments(corners: ImageCorners(radius: cornerRadius), imageSize: dimensions.aspectFilled(contentImageSize), boundingSize: contentImageSize, intrinsicInsets: UIEdgeInsets())) applyImageLayout() if let updateImageSignal = updateImageSignal { @@ -384,6 +518,7 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { let _ = labelApply() let _ = viewsApply() let _ = forwardsApply() + let _ = reactionsApply() switch item.style { case .plain: @@ -427,7 +562,7 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { let bottomStripeInset: CGFloat switch neighbors.bottom { case .sameSection(false): - bottomStripeInset = leftInset + bottomStripeInset = totalLeftInset strongSelf.bottomStripeNode.isHidden = false default: bottomStripeInset = 0.0 @@ -443,22 +578,128 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight)) } - let contentImageFrame = CGRect(origin: CGPoint(x: leftInset, y: floorToScreenPixels((height - contentImageSize.height) / 2.0)), size: contentImageSize) + let contentImageFrame = CGRect(origin: CGPoint(x: contentImageInset, y: floorToScreenPixels((height - contentImageSize.height) / 2.0)), size: contentImageSize) strongSelf.contentImageNode.frame = contentImageFrame - let titleFrame = CGRect(origin: CGPoint(x: totalLeftInset, y: 11.0), size: titleLayout.size) + if let avatarNode = strongSelf.avatarNode { + avatarNode.frame = contentImageFrame + } + + let titleFrame = CGRect(origin: CGPoint(x: totalLeftInset, y: 9.0), size: titleLayout.size) strongSelf.titleNode.frame = titleFrame let labelFrame = CGRect(origin: CGPoint(x: totalLeftInset, y: titleFrame.maxY + titleSpacing), size: labelLayout.size) strongSelf.labelNode.frame = labelFrame - let viewsFrame = CGRect(origin: CGPoint(x: params.width - rightInset - viewsLayout.size.width, y: 15.0), size: viewsLayout.size) + let viewsOriginY: CGFloat = forwardsLayout.size.width > 0.0 || reactionsLayout.size.width > 0.0 ? 13.0 : floorToScreenPixels((contentSize.height - viewsLayout.size.height) / 2.0) + let viewsFrame = CGRect(origin: CGPoint(x: params.width - rightInset - viewsLayout.size.width, y: viewsOriginY), size: viewsLayout.size) strongSelf.viewsNode.frame = viewsFrame - let forwardsFrame = CGRect(origin: CGPoint(x: params.width - rightInset - forwardsLayout.size.width, y: titleFrame.maxY + titleSpacing), size: forwardsLayout.size) - strongSelf.forwardsNode.frame = forwardsFrame - + let iconSpacing: CGFloat = 3.0 - UIScreenPixel + + var rightContentInset: CGFloat = rightInset + if forwardsLayout.size.width > 0.0 { + strongSelf.forwardsIconNode.isHidden = false + strongSelf.forwardsNode.isHidden = false + + let forwardsFrame = CGRect(origin: CGPoint(x: params.width - rightContentInset - forwardsLayout.size.width, y: titleFrame.maxY + titleSpacing), size: forwardsLayout.size) + strongSelf.forwardsNode.frame = forwardsFrame + + if let icon = strongSelf.forwardsIconNode.image { + let forwardsIconFrame = CGRect(origin: CGPoint(x: params.width - rightContentInset - forwardsLayout.size.width - icon.size.width - iconSpacing, y: titleFrame.maxY + titleSpacing - 2.0 + UIScreenPixel), size: icon.size) + strongSelf.forwardsIconNode.frame = forwardsIconFrame + + rightContentInset += forwardsIconFrame.width + forwardsFrame.width + iconSpacing + } + rightContentInset += 10.0 + } else { + strongSelf.forwardsIconNode.isHidden = true + strongSelf.forwardsNode.isHidden = true + } + + if reactionsLayout.size.width > 0.0 { + strongSelf.reactionsIconNode.isHidden = false + strongSelf.reactionsNode.isHidden = false + + let reactionsFrame = CGRect(origin: CGPoint(x: params.width - rightContentInset - reactionsLayout.size.width, y: titleFrame.maxY + titleSpacing), size: reactionsLayout.size) + strongSelf.reactionsNode.frame = reactionsFrame + + if let icon = strongSelf.reactionsIconNode.image { + let reactionsIconFrame = CGRect(origin: CGPoint(x: params.width - rightContentInset - reactionsLayout.size.width - icon.size.width - iconSpacing, y: titleFrame.maxY + titleSpacing - 2.0 + UIScreenPixel), size: icon.size) + strongSelf.reactionsIconNode.frame = reactionsIconFrame + + rightContentInset += reactionsIconFrame.width + reactionsFrame.width + iconSpacing + } + } else { + strongSelf.reactionsIconNode.isHidden = true + strongSelf.reactionsNode.isHidden = true + } + strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: height + UIScreenPixel)) + + if case .story = item.item { + let lineWidth: CGFloat = 1.5 + let imageSize = CGSize(width: contentImageFrame.width + 6.0, height: contentImageFrame.height + 6.0) + let indicatorSize = CGSize(width: imageSize.width - lineWidth * 4.0, height: imageSize.height - lineWidth * 4.0) + + let storyIndicator: ComponentView + let indicatorTransition: Transition = .immediate + if let current = strongSelf.storyIndicator { + storyIndicator = current + } else { + storyIndicator = ComponentView() + strongSelf.storyIndicator = storyIndicator + } + let _ = storyIndicator.update( + transition: indicatorTransition, + component: AnyComponent(AvatarStoryIndicatorComponent( + hasUnseen: true, + hasUnseenCloseFriendsItems: false, + colors: AvatarStoryIndicatorComponent.Colors( + unseenColors: item.presentationData.theme.chatList.storyUnseenColors.array, + unseenCloseFriendsColors: item.presentationData.theme.chatList.storyUnseenPrivateColors.array, + seenColors: item.presentationData.theme.chatList.storySeenColors.array + ), + activeLineWidth: lineWidth, + inactiveLineWidth: lineWidth, + counters: AvatarStoryIndicatorComponent.Counters( + totalCount: 1, + unseenCount: 1 + ), + progress: nil + )), + environment: {}, + containerSize: indicatorSize + ) + let storyIndicatorFrame = CGRect(origin: CGPoint(x: contentImageFrame.midX - indicatorSize.width / 2.0, y: contentImageFrame.midY - indicatorSize.height / 2.0), size: indicatorSize) + if let storyIndicatorView = storyIndicator.view { + if storyIndicatorView.superview == nil { + strongSelf.offsetContainerNode.view.addSubview(storyIndicatorView) + } + indicatorTransition.setFrame(view: storyIndicatorView, frame: storyIndicatorFrame) + } + + let storyButton: HighlightTrackingButton + if let current = strongSelf.storyButton { + storyButton = current + } else { + storyButton = HighlightTrackingButton() + storyButton.addTarget(strongSelf, action: #selector(strongSelf.storyPressed), for: .touchUpInside) + strongSelf.view.addSubview(storyButton) + strongSelf.storyButton = storyButton + } + storyButton.frame = storyIndicatorFrame + } else if let storyIndicator = strongSelf.storyIndicator { + if let storyIndicatorView = storyIndicator.view { + storyIndicatorView.removeFromSuperview() + } + strongSelf.storyIndicator = nil + + if let storyButton = strongSelf.storyButton { + storyButton.removeFromSuperview() + strongSelf.storyButton = nil + } + } } }) } @@ -467,6 +708,11 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { override public func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) { super.setHighlighted(highlighted, at: point, animated: animated) + var highlighted = highlighted + if let avatarButton = self.storyButton, avatarButton.bounds.contains(self.view.convert(point, to: storyButton)) { + highlighted = false + } + if highlighted { self.highlightedBackgroundNode.alpha = 1.0 if self.highlightedBackgroundNode.supernode == nil { @@ -514,4 +760,3 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) } } - diff --git a/submodules/StatisticsUI/Sources/StatsOverviewItem.swift b/submodules/StatisticsUI/Sources/StatsOverviewItem.swift index 5021d837797..4810e266b46 100644 --- a/submodules/StatisticsUI/Sources/StatsOverviewItem.swift +++ b/submodules/StatisticsUI/Sources/StatsOverviewItem.swift @@ -8,31 +8,43 @@ import TelegramPresentationData import ItemListUI import PresentationDataUtils -protocol PeerStats { +protocol Stats { } -extension ChannelStats: PeerStats { +extension ChannelStats: Stats { } -extension GroupStats: PeerStats { +extension GroupStats: Stats { } -extension ChannelBoostStatus: PeerStats { +extension ChannelBoostStatus: Stats { + +} + +extension MessageStats: Stats { + +} + +extension StoryStats: Stats { } class StatsOverviewItem: ListViewItem, ItemListItem { let presentationData: ItemListPresentationData - let stats: PeerStats + let stats: Stats + let storyViews: EngineStoryItem.Views? + let publicShares: Int32? let sectionId: ItemListSectionId let style: ItemListStyle - init(presentationData: ItemListPresentationData, stats: PeerStats, sectionId: ItemListSectionId, style: ItemListStyle) { + init(presentationData: ItemListPresentationData, stats: Stats, storyViews: EngineStoryItem.Views? = nil, publicShares: Int32? = nil, sectionId: ItemListSectionId, style: ItemListStyle) { self.presentationData = presentationData self.stats = stats + self.storyViews = storyViews + self.publicShares = publicShares self.sectionId = sectionId self.style = style } @@ -73,26 +85,133 @@ class StatsOverviewItem: ListViewItem, ItemListItem { var selectable: Bool = false } +private final class ValueItemNode: ASDisplayNode { + enum DeltaColor { + case generic + case positive + case negative + } + + private let valueNode: TextNode + private let titleNode: TextNode + private let deltaNode: TextNode + + var currentBackgroundColor: UIColor? + var pressed: (() -> Void)? + + override init() { + self.valueNode = TextNode() + self.titleNode = TextNode() + self.deltaNode = TextNode() + + super.init() + + self.isUserInteractionEnabled = false + + self.addSubnode(self.valueNode) + self.addSubnode(self.titleNode) + self.addSubnode(self.deltaNode) + } + + static func asyncLayout(_ current: ValueItemNode?) -> (_ width: CGFloat, _ presentationData: ItemListPresentationData, _ value: String, _ title: String, _ delta: (String, DeltaColor)?) -> (CGSize, () -> ValueItemNode) { + + let maybeMakeValueLayout = (current?.valueNode).flatMap(TextNode.asyncLayout) + let maybeMakeTitleLayout = (current?.titleNode).flatMap(TextNode.asyncLayout) + let maybeMakeDeltaLayout = (current?.deltaNode).flatMap(TextNode.asyncLayout) + + return { width, presentationData, value, title, delta in + let targetNode: ValueItemNode + if let current = current { + targetNode = current + } else { + targetNode = ValueItemNode() + } + + let makeValueLayout: (TextNodeLayoutArguments) -> (TextNodeLayout, () -> TextNode) + if let maybeMakeValueLayout { + makeValueLayout = maybeMakeValueLayout + } else { + makeValueLayout = TextNode.asyncLayout(targetNode.valueNode) + } + + let makeTitleLayout: (TextNodeLayoutArguments) -> (TextNodeLayout, () -> TextNode) + if let maybeMakeTitleLayout { + makeTitleLayout = maybeMakeTitleLayout + } else { + makeTitleLayout = TextNode.asyncLayout(targetNode.titleNode) + } + + let makeDeltaLayout: (TextNodeLayoutArguments) -> (TextNodeLayout, () -> TextNode) + if let maybeMakeDeltaLayout { + makeDeltaLayout = maybeMakeDeltaLayout + } else { + makeDeltaLayout = TextNode.asyncLayout(targetNode.deltaNode) + } + + let valueFont = Font.semibold(presentationData.fontSize.itemListBaseFontSize) + let titleFont = Font.regular(presentationData.fontSize.itemListBaseHeaderFontSize) + let deltaFont = Font.regular(presentationData.fontSize.itemListBaseHeaderFontSize) + + let valueColor = presentationData.theme.list.itemPrimaryTextColor + let titleColor = presentationData.theme.list.sectionHeaderTextColor + + let deltaColor: UIColor + if let (_, color) = delta { + switch color { + case .generic: + deltaColor = titleColor + case .positive: + deltaColor = presentationData.theme.list.freeTextSuccessColor + case .negative: + deltaColor = presentationData.theme.list.freeTextErrorColor + } + } else { + deltaColor = presentationData.theme.list.freeTextErrorColor + } + + let constrainedSize = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude) + let (valueLayout, valueApply) = makeValueLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: value, font: valueFont, textColor: valueColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: constrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + + let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: titleFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: constrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + + let (deltaLayout, deltaApply) = makeDeltaLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: delta?.0 ?? "", font: deltaFont, textColor: deltaColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: constrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + + let horizontalSpacing: CGFloat = 4.0 + let size = CGSize(width: valueLayout.size.width + horizontalSpacing + deltaLayout.size.width, height: valueLayout.size.height + titleLayout.size.height) + return (size, { + let _ = valueApply() + let _ = titleApply() + let _ = deltaApply() + + let valueFrame = CGRect(origin: .zero, size: valueLayout.size) + let titleFrame = CGRect(origin: CGPoint(x: 0.0, y: valueFrame.maxY), size: titleLayout.size) + let deltaFrame = CGRect(origin: CGPoint(x: valueFrame.maxX + horizontalSpacing, y: valueFrame.maxY - deltaLayout.size.height - 2.0), size: deltaLayout.size) + + targetNode.valueNode.frame = valueFrame + targetNode.titleNode.frame = titleFrame + targetNode.deltaNode.frame = deltaFrame + + return targetNode + }) + } + } +} + + class StatsOverviewItemNode: ListViewItemNode { private let backgroundNode: ASDisplayNode private let topStripeNode: ASDisplayNode private let bottomStripeNode: ASDisplayNode private let maskNode: ASImageNode - private let topLeftValueLabel: ImmediateTextNode - private let bottomLeftValueLabel: ImmediateTextNode - private let bottomRightValueLabel: ImmediateTextNode - private let topRightValueLabel: ImmediateTextNode - - private let topLeftTitleLabel: ImmediateTextNode - private let bottomLeftTitleLabel: ImmediateTextNode - private let bottomRightTitleLabel: ImmediateTextNode - private let topRightTitleLabel: ImmediateTextNode - - private let topLeftDeltaLabel: ImmediateTextNode - private let bottomLeftDeltaLabel: ImmediateTextNode - private let bottomRightDeltaLabel: ImmediateTextNode - private let topRightDeltaLabel: ImmediateTextNode + private let topLeftItem: ValueItemNode + private let topRightItem: ValueItemNode + private let middle1LeftItem: ValueItemNode + private let middle1RightItem: ValueItemNode + private let middle2LeftItem: ValueItemNode + private let middle2RightItem: ValueItemNode + private let bottomLeftItem: ValueItemNode + private let bottomRightItem: ValueItemNode private var item: StatsOverviewItem? @@ -109,56 +228,38 @@ class StatsOverviewItemNode: ListViewItemNode { self.maskNode = ASImageNode() - self.topLeftValueLabel = ImmediateTextNode() - self.bottomLeftValueLabel = ImmediateTextNode() - self.bottomRightValueLabel = ImmediateTextNode() - self.topRightValueLabel = ImmediateTextNode() - - self.topLeftTitleLabel = ImmediateTextNode() - self.bottomLeftTitleLabel = ImmediateTextNode() - self.bottomRightTitleLabel = ImmediateTextNode() - self.topRightTitleLabel = ImmediateTextNode() - - self.topLeftDeltaLabel = ImmediateTextNode() - self.bottomLeftDeltaLabel = ImmediateTextNode() - self.bottomRightDeltaLabel = ImmediateTextNode() - self.topRightDeltaLabel = ImmediateTextNode() + self.topLeftItem = ValueItemNode() + self.topRightItem = ValueItemNode() + self.middle1LeftItem = ValueItemNode() + self.middle1RightItem = ValueItemNode() + self.middle2LeftItem = ValueItemNode() + self.middle2RightItem = ValueItemNode() + self.bottomLeftItem = ValueItemNode() + self.bottomRightItem = ValueItemNode() super.init(layerBacked: false, dynamicBounce: false) self.clipsToBounds = true - self.addSubnode(self.topLeftValueLabel) - self.addSubnode(self.bottomLeftValueLabel) - self.addSubnode(self.bottomRightValueLabel) - self.addSubnode(self.topRightValueLabel) - - self.addSubnode(self.topLeftTitleLabel) - self.addSubnode(self.bottomLeftTitleLabel) - self.addSubnode(self.bottomRightTitleLabel) - self.addSubnode(self.topRightTitleLabel) - - self.addSubnode(self.topLeftDeltaLabel) - self.addSubnode(self.bottomLeftDeltaLabel) - self.addSubnode(self.bottomRightDeltaLabel) - self.addSubnode(self.topRightDeltaLabel) + self.addSubnode(self.topLeftItem) + self.addSubnode(self.topRightItem) + self.addSubnode(self.middle1LeftItem) + self.addSubnode(self.middle1RightItem) + self.addSubnode(self.middle2LeftItem) + self.addSubnode(self.middle2RightItem) + self.addSubnode(self.bottomLeftItem) + self.addSubnode(self.bottomRightItem) } func asyncLayout() -> (_ item: StatsOverviewItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { - let makeTopLeftValueLabelLayout = TextNode.asyncLayout(self.topLeftValueLabel) - let makeTopRightValueLabelLayout = TextNode.asyncLayout(self.topRightValueLabel) - let makeBottomLeftValueLabelLayout = TextNode.asyncLayout(self.bottomLeftValueLabel) - let makeBottomRightValueLabelLayout = TextNode.asyncLayout(self.bottomRightValueLabel) - - let makeTopLeftTitleLabelLayout = TextNode.asyncLayout(self.topLeftTitleLabel) - let makeTopRightTitleLabelLayout = TextNode.asyncLayout(self.topRightTitleLabel) - let makeBottomLeftTitleLabelLayout = TextNode.asyncLayout(self.bottomLeftTitleLabel) - let makeBottomRightTitleLabelLayout = TextNode.asyncLayout(self.bottomRightTitleLabel) - - let makeTopLeftDeltaLabelLayout = TextNode.asyncLayout(self.topLeftDeltaLabel) - let makeTopRightDeltaLabelLayout = TextNode.asyncLayout(self.topRightDeltaLabel) - let makeBottomLeftDeltaLabelLayout = TextNode.asyncLayout(self.bottomLeftDeltaLabel) - let makeBottomRightDeltaLabelLayout = TextNode.asyncLayout(self.bottomRightDeltaLabel) + let makeTopLeftItemLayout = ValueItemNode.asyncLayout(self.topLeftItem) + let makeTopRightItemLayout = ValueItemNode.asyncLayout(self.topRightItem) + let makeMiddle1LeftItemLayout = ValueItemNode.asyncLayout(self.middle1LeftItem) + let makeMiddle1RightItemLayout = ValueItemNode.asyncLayout(self.middle1RightItem) + let makeMiddle2LeftItemLayout = ValueItemNode.asyncLayout(self.middle2LeftItem) + let makeMiddle2RightItemLayout = ValueItemNode.asyncLayout(self.middle2RightItem) + let makeBottomLeftItemLayout = ValueItemNode.asyncLayout(self.bottomLeftItem) + let makeBottomRightItemLayout = ValueItemNode.asyncLayout(self.bottomRightItem) let currentItem = self.item @@ -176,7 +277,6 @@ class StatsOverviewItemNode: ListViewItemNode { var height: CGFloat = topInset * 2.0 let leftInset = params.leftInset - let rightInset: CGFloat = params.rightInset var updatedTheme: PresentationTheme? if currentItem?.presentationData.theme !== item.presentationData.theme { @@ -194,28 +294,18 @@ class StatsOverviewItemNode: ListViewItemNode { insets = itemListNeighborsGroupedInsets(neighbors, params) } - let valueFont = Font.semibold(item.presentationData.fontSize.itemListBaseFontSize) - let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseHeaderFontSize) - let deltaFont = Font.regular(item.presentationData.fontSize.itemListBaseHeaderFontSize) + let twoColumnLayout = "".isEmpty - let topLeftValueLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))? - let topRightValueLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))? - let bottomLeftValueLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))? - let bottomRightValueLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))? - - let topLeftTitleLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))? - let topRightTitleLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))? - let bottomLeftTitleLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))? - let bottomRightTitleLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))? + var topLeftItemLayoutAndApply: (CGSize, () -> ValueItemNode)? + var topRightItemLayoutAndApply: (CGSize, () -> ValueItemNode)? + var middle1LeftItemLayoutAndApply: (CGSize, () -> ValueItemNode)? + var middle1RightItemLayoutAndApply: (CGSize, () -> ValueItemNode)? + var middle2LeftItemLayoutAndApply: (CGSize, () -> ValueItemNode)? + var middle2RightItemLayoutAndApply: (CGSize, () -> ValueItemNode)? + var bottomLeftItemLayoutAndApply: (CGSize, () -> ValueItemNode)? + var bottomRightItemLayoutAndApply: (CGSize, () -> ValueItemNode)? - let topLeftDeltaLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))? - let topRightDeltaLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))? - let bottomLeftDeltaLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))? - let bottomRightDeltaLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))? - - var twoColumnLayout = true - - func deltaText(_ value: StatsValue) -> (String, Bool, Bool) { + func deltaText(_ value: StatsValue) -> (text: String, positive: Bool, hasValue: Bool) { let deltaValue = value.current - value.previous let deltaCompact = compactNumericCountString(abs(Int(deltaValue))) let delta = deltaValue > 0 ? "+\(deltaCompact)" : "-\(deltaCompact)" @@ -227,12 +317,103 @@ class StatsOverviewItemNode: ListViewItemNode { return (abs(deltaPercentage) > 0.0 ? String(format: "%@ (%.02f%%)", delta, deltaPercentage * 100.0) : "", deltaValue > 0.0, abs(deltaValue) > 0.0) } - if let stats = item.stats as? ChannelBoostStatus { - topLeftValueLabelLayoutAndApply = makeTopLeftValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "\(stats.level)", font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - topRightValueLabelLayoutAndApply = makeTopRightValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "≈\(Int(stats.premiumAudience?.value ?? 0))", font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + if let stats = item.stats as? MessageStats { + topLeftItemLayoutAndApply = makeTopLeftItemLayout( + params.width, + item.presentationData, + compactNumericCountString(stats.views), + item.presentationData.strings.Stats_Message_Views, + nil + ) + + topRightItemLayoutAndApply = makeTopRightItemLayout( + params.width, + item.presentationData, + item.publicShares.flatMap { compactNumericCountString(Int($0)) } ?? "–", + item.presentationData.strings.Stats_Message_PublicShares, + nil + ) + + middle1LeftItemLayoutAndApply = makeMiddle1LeftItemLayout( + params.width, + item.presentationData, + compactNumericCountString(stats.reactions), + item.presentationData.strings.Stats_Message_Reactions, + nil + ) + + middle1RightItemLayoutAndApply = makeMiddle1RightItemLayout( + params.width, + item.presentationData, + item.publicShares.flatMap { "≈\( compactNumericCountString(max(0, stats.forwards - Int($0))))" } ?? "–", + item.presentationData.strings.Stats_Message_PrivateShares, + nil + ) + + height += topRightItemLayoutAndApply!.0.height * 2.0 + verticalSpacing + } else if let _ = item.stats as? StoryStats, let views = item.storyViews { + topLeftItemLayoutAndApply = makeTopLeftItemLayout( + params.width, + item.presentationData, + compactNumericCountString(views.seenCount), + item.presentationData.strings.Stats_Message_Views, + nil + ) + + topRightItemLayoutAndApply = makeTopRightItemLayout( + params.width, + item.presentationData, + item.publicShares.flatMap { compactNumericCountString(Int($0)) } ?? "–", + item.presentationData.strings.Stats_Message_PublicShares, + nil + ) + + middle1LeftItemLayoutAndApply = makeMiddle1LeftItemLayout( + params.width, + item.presentationData, + compactNumericCountString(views.reactedCount), + item.presentationData.strings.Stats_Message_Reactions, + nil + ) + + middle1RightItemLayoutAndApply = makeMiddle1RightItemLayout( + params.width, + item.presentationData, + compactNumericCountString(views.forwardCount - Int(item.publicShares ?? 0)), + item.presentationData.strings.Stats_Message_PrivateShares, + nil + ) + + height += topRightItemLayoutAndApply!.0.height * 2.0 + verticalSpacing + } else if let stats = item.stats as? ChannelBoostStatus { + topLeftItemLayoutAndApply = makeTopLeftItemLayout( + params.width, + item.presentationData, + "\(stats.level)", + item.presentationData.strings.Stats_Boosts_Level, + nil + ) - bottomLeftValueLabelLayoutAndApply = makeBottomLeftValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "\(stats.boosts)", font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + var premiumSubscribers: Double = 0.0 + if let premiumAudience = stats.premiumAudience, premiumAudience.total > 0 { + premiumSubscribers = premiumAudience.value / premiumAudience.total + } + + topRightItemLayoutAndApply = makeTopRightItemLayout( + params.width, + item.presentationData, + "≈\(Int(stats.premiumAudience?.value ?? 0))", + item.presentationData.strings.Stats_Boosts_PremiumSubscribers, + (String(format: "%.02f%%", premiumSubscribers * 100.0), .generic) + ) + + middle1LeftItemLayoutAndApply = makeMiddle1LeftItemLayout( + params.width, + item.presentationData, + "\(stats.boosts)", + item.presentationData.strings.Stats_Boosts_ExistingBoosts, + nil + ) let boostsLeft: Int32 if let nextLevelBoosts = stats.nextLevelBoosts { @@ -240,162 +421,202 @@ class StatsOverviewItemNode: ListViewItemNode { } else { boostsLeft = 0 } + middle1RightItemLayoutAndApply = makeMiddle1RightItemLayout( + params.width, + item.presentationData, + "\(boostsLeft)", + item.presentationData.strings.Stats_Boosts_BoostsToLevelUp, + nil + ) - bottomRightValueLabelLayoutAndApply = makeBottomRightValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "\(boostsLeft)", font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - topLeftTitleLabelLayoutAndApply = makeTopLeftTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_Boosts_Level, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - topRightTitleLabelLayoutAndApply = makeTopRightTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_Boosts_PremiumSubscribers, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - bottomLeftTitleLabelLayoutAndApply = makeBottomLeftTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_Boosts_ExistingBoosts, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - bottomRightTitleLabelLayoutAndApply = makeBottomRightTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_Boosts_BoostsToLevelUp, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - - topLeftDeltaLabelLayoutAndApply = nil - - var premiumSubscribers: Double = 0.0 - if let premiumAudience = stats.premiumAudience, premiumAudience.total > 0 { - premiumSubscribers = premiumAudience.value / premiumAudience.total + if twoColumnLayout { + height += topRightItemLayoutAndApply!.0.height * 2.0 + verticalSpacing + } else { + height += topLeftItemLayoutAndApply!.0.height * 4.0 + verticalSpacing * 3.0 } - - topRightDeltaLabelLayoutAndApply = makeTopRightDeltaLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: String(format: "%.02f%%", premiumSubscribers * 100.0), font: deltaFont, textColor: item.presentationData.theme.list.freeTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - bottomLeftDeltaLabelLayoutAndApply = nil - bottomRightDeltaLabelLayoutAndApply = nil - - height += topRightValueLabelLayoutAndApply!.0.size.height + topRightTitleLabelLayoutAndApply!.0.size.height - - height += verticalSpacing - height += bottomRightValueLabelLayoutAndApply!.0.size.height + bottomRightTitleLabelLayoutAndApply!.0.size.height } else if let stats = item.stats as? ChannelStats { let viewsPerPostDelta = deltaText(stats.viewsPerPost) let sharesPerPostDelta = deltaText(stats.sharesPerPost) + let reactionsPerPostDelta = deltaText(stats.reactionsPerPost) + + let viewsPerStoryDelta = deltaText(stats.viewsPerStory) + let sharesPerStoryDelta = deltaText(stats.sharesPerStory) + let reactionsPerStoryDelta = deltaText(stats.reactionsPerStory) - let displayBottomRow = stats.sharesPerPost.current > 0 || viewsPerPostDelta.2 || stats.viewsPerPost.current > 0 || sharesPerPostDelta.2 + let followersDelta = deltaText(stats.followers) + topLeftItemLayoutAndApply = makeTopLeftItemLayout( + params.width, + item.presentationData, + compactNumericCountString(Int(stats.followers.current)), + item.presentationData.strings.Stats_Followers, + (followersDelta.text, followersDelta.positive ? .positive : .negative) + ) - topLeftValueLabelLayoutAndApply = makeTopLeftValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: compactNumericCountString(Int(stats.followers.current)), font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - var enabledNotifications: Double = 0.0 if stats.enabledNotifications.total > 0 { enabledNotifications = stats.enabledNotifications.value / stats.enabledNotifications.total } - - topRightValueLabelLayoutAndApply = makeTopRightValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: String(format: "%.02f%%", enabledNotifications * 100.0), font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - bottomLeftValueLabelLayoutAndApply = makeBottomLeftValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: displayBottomRow ? compactNumericCountString(Int(stats.viewsPerPost.current)) : "", font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - bottomRightValueLabelLayoutAndApply = makeBottomRightValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: displayBottomRow ? compactNumericCountString(Int(stats.sharesPerPost.current)) : "", font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - topLeftTitleLabelLayoutAndApply = makeTopLeftTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_Followers, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - topRightTitleLabelLayoutAndApply = makeTopRightTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_EnabledNotifications, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - bottomLeftTitleLabelLayoutAndApply = makeBottomLeftTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: displayBottomRow ? item.presentationData.strings.Stats_ViewsPerPost : "", font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - bottomRightTitleLabelLayoutAndApply = makeBottomRightTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: displayBottomRow ? item.presentationData.strings.Stats_SharesPerPost : "", font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - let followersDelta = deltaText(stats.followers) - topLeftDeltaLabelLayoutAndApply = makeTopLeftDeltaLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: followersDelta.0, font: deltaFont, textColor: followersDelta.1 ? item.presentationData.theme.list.freeTextSuccessColor : item.presentationData.theme.list.freeTextErrorColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - topRightDeltaLabelLayoutAndApply = nil - - bottomLeftDeltaLabelLayoutAndApply = makeBottomLeftDeltaLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: viewsPerPostDelta.0, font: deltaFont, textColor: viewsPerPostDelta.1 ? item.presentationData.theme.list.freeTextSuccessColor : item.presentationData.theme.list.freeTextErrorColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - bottomRightDeltaLabelLayoutAndApply = makeBottomRightDeltaLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: sharesPerPostDelta.0, font: deltaFont, textColor: sharesPerPostDelta.1 ? item.presentationData.theme.list.freeTextSuccessColor : item.presentationData.theme.list.freeTextErrorColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - - height += topRightValueLabelLayoutAndApply!.0.size.height + topRightTitleLabelLayoutAndApply!.0.size.height - - if max(topLeftValueLabelLayoutAndApply!.0.size.width + topLeftDeltaLabelLayoutAndApply!.0.size.width + horizontalSpacing + topRightValueLabelLayoutAndApply!.0.size.width, bottomLeftValueLabelLayoutAndApply!.0.size.width + bottomLeftDeltaLabelLayoutAndApply!.0.size.width + horizontalSpacing + bottomRightValueLabelLayoutAndApply!.0.size.width + bottomRightDeltaLabelLayoutAndApply!.0.size.width) > params.width - leftInset - rightInset { - twoColumnLayout = false + topRightItemLayoutAndApply = makeTopRightItemLayout( + params.width, + item.presentationData, + String(format: "%.02f%%", enabledNotifications * 100.0), + item.presentationData.strings.Stats_EnabledNotifications, + nil + ) + + let hasMessages = stats.viewsPerPost.current > 0 + let hasStories = stats.viewsPerStory.current > 0 || viewsPerStoryDelta.hasValue + + var items: [Int: (String, String, (String, ValueItemNode.DeltaColor)?)] = [:] + if hasMessages { + items[0] = ( + compactNumericCountString(Int(stats.viewsPerPost.current)), + item.presentationData.strings.Stats_ViewsPerPost, + (viewsPerPostDelta.text, viewsPerPostDelta.positive ? .positive : .negative) + ) + } + if hasMessages { + let index = hasStories ? 2 : 1 + items[index] = ( + compactNumericCountString(Int(stats.sharesPerPost.current)), + item.presentationData.strings.Stats_SharesPerPost, + (sharesPerPostDelta.text, sharesPerPostDelta.positive ? .positive : .negative) + ) + } + if stats.reactionsPerPost.current > 0 || reactionsPerStoryDelta.hasValue { + let index = hasStories ? 4 : 2 + items[index] = ( + compactNumericCountString(Int(stats.reactionsPerPost.current)), + item.presentationData.strings.Stats_ReactionsPerPost, + (reactionsPerPostDelta.text, reactionsPerPostDelta.positive ? .positive : .negative) + ) + } + if hasStories { + items[1] = ( + compactNumericCountString(Int(stats.viewsPerStory.current)), + item.presentationData.strings.Stats_ViewsPerStory, + (viewsPerStoryDelta.text, viewsPerStoryDelta.positive ? .positive : .negative) + ) + items[3] = ( + compactNumericCountString(Int(stats.sharesPerStory.current)), + item.presentationData.strings.Stats_SharesPerStory, + (sharesPerStoryDelta.text, sharesPerStoryDelta.positive ? .positive : .negative) + ) + items[5] = ( + compactNumericCountString(Int(stats.reactionsPerStory.current)), + item.presentationData.strings.Stats_ReactionsPerStory, + (reactionsPerStoryDelta.text, reactionsPerStoryDelta.positive ? .positive : .negative) + ) } + if let (value, title, delta) = items[0] { + middle1LeftItemLayoutAndApply = makeMiddle1LeftItemLayout( + params.width, + item.presentationData, + value, + title, + delta + ) + } + if let (value, title, delta) = items[1] { + middle1RightItemLayoutAndApply = makeMiddle1RightItemLayout( + params.width, + item.presentationData, + value, + title, + delta + ) + } + if let (value, title, delta) = items[2] { + middle2LeftItemLayoutAndApply = makeMiddle2LeftItemLayout( + params.width, + item.presentationData, + value, + title, + delta + ) + } + if let (value, title, delta) = items[3] { + middle2RightItemLayoutAndApply = makeMiddle2RightItemLayout( + params.width, + item.presentationData, + value, + title, + delta + ) + } + if let (value, title, delta) = items[4] { + bottomLeftItemLayoutAndApply = makeBottomLeftItemLayout( + params.width, + item.presentationData, + value, + title, + delta + ) + } + if let (value, title, delta) = items[5] { + bottomRightItemLayoutAndApply = makeBottomRightItemLayout( + params.width, + item.presentationData, + value, + title, + delta + ) + } + + let valuesCount = CGFloat(2 + items.count) if twoColumnLayout { - if displayBottomRow { - height += verticalSpacing - height += bottomRightValueLabelLayoutAndApply!.0.size.height + bottomRightTitleLabelLayoutAndApply!.0.size.height - } + let rowsCount = ceil(valuesCount / 2.0) + height += topLeftItemLayoutAndApply!.0.height * rowsCount + (verticalSpacing * (rowsCount - 1.0)) } else { - height += verticalSpacing - height += topRightValueLabelLayoutAndApply!.0.size.height + topRightTitleLabelLayoutAndApply!.0.size.height - if !stats.viewsPerPost.current.isZero || viewsPerPostDelta.2 { - height += verticalSpacing - height += bottomLeftValueLabelLayoutAndApply!.0.size.height + bottomLeftTitleLabelLayoutAndApply!.0.size.height - } - if !stats.sharesPerPost.current.isZero || sharesPerPostDelta.2 { - height += verticalSpacing - height += bottomRightValueLabelLayoutAndApply!.0.size.height + bottomRightTitleLabelLayoutAndApply!.0.size.height - } + height += topLeftItemLayoutAndApply!.0.height * valuesCount + (verticalSpacing * (valuesCount - 1.0)) } } else if let stats = item.stats as? GroupStats { let viewersDelta = deltaText(stats.viewers) let postersDelta = deltaText(stats.posters) let displayBottomRow = stats.viewers.current > 0 || viewersDelta.2 || stats.posters.current > 0 || postersDelta.2 - - topLeftValueLabelLayoutAndApply = makeTopLeftValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: compactNumericCountString(Int(stats.members.current)), font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - topRightValueLabelLayoutAndApply = makeTopRightValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: compactNumericCountString(Int(stats.messages.current)), font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - bottomLeftValueLabelLayoutAndApply = makeBottomLeftValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: displayBottomRow ? compactNumericCountString(Int(stats.viewers.current)) : "", font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - bottomRightValueLabelLayoutAndApply = makeBottomRightValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: displayBottomRow ? compactNumericCountString(Int(stats.posters.current)) : "", font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - topLeftTitleLabelLayoutAndApply = makeTopLeftTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_GroupMembers, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - topRightTitleLabelLayoutAndApply = makeTopRightTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_GroupMessages, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - bottomLeftTitleLabelLayoutAndApply = makeBottomLeftTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: displayBottomRow ? item.presentationData.strings.Stats_GroupViewers : "", font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - bottomRightTitleLabelLayoutAndApply = makeBottomRightTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: displayBottomRow ? item.presentationData.strings.Stats_GroupPosters : "", font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - + let membersDelta = deltaText(stats.members) - topLeftDeltaLabelLayoutAndApply = makeTopLeftDeltaLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: membersDelta.0, font: deltaFont, textColor: membersDelta.1 ? item.presentationData.theme.list.freeTextSuccessColor : item.presentationData.theme.list.freeTextErrorColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + topLeftItemLayoutAndApply = makeTopLeftItemLayout( + params.width, + item.presentationData, + compactNumericCountString(Int(stats.members.current)), + item.presentationData.strings.Stats_GroupMembers, + (membersDelta.text, membersDelta.positive ? .positive : .negative) + ) let messagesDelta = deltaText(stats.messages) - topRightDeltaLabelLayoutAndApply = makeTopRightDeltaLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: messagesDelta.0, font: deltaFont, textColor: messagesDelta.1 ? item.presentationData.theme.list.freeTextSuccessColor : item.presentationData.theme.list.freeTextErrorColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - bottomLeftDeltaLabelLayoutAndApply = makeBottomLeftDeltaLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: viewersDelta.0, font: deltaFont, textColor: viewersDelta.1 ? item.presentationData.theme.list.freeTextSuccessColor : item.presentationData.theme.list.freeTextErrorColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - bottomRightDeltaLabelLayoutAndApply = makeBottomRightDeltaLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: postersDelta.0, font: deltaFont, textColor: postersDelta.1 ? item.presentationData.theme.list.freeTextSuccessColor : item.presentationData.theme.list.freeTextErrorColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - - height += topRightValueLabelLayoutAndApply!.0.size.height + topRightTitleLabelLayoutAndApply!.0.size.height - - if max(topLeftValueLabelLayoutAndApply!.0.size.width + topLeftDeltaLabelLayoutAndApply!.0.size.width + horizontalSpacing + topRightValueLabelLayoutAndApply!.0.size.width, bottomLeftValueLabelLayoutAndApply!.0.size.width + bottomLeftDeltaLabelLayoutAndApply!.0.size.width + horizontalSpacing + bottomRightValueLabelLayoutAndApply!.0.size.width + bottomRightDeltaLabelLayoutAndApply!.0.size.width) > params.width - leftInset - rightInset { - twoColumnLayout = false + topRightItemLayoutAndApply = makeTopRightItemLayout( + params.width, + item.presentationData, + compactNumericCountString(Int(stats.messages.current)), + item.presentationData.strings.Stats_GroupMessages, + (messagesDelta.text, messagesDelta.positive ? .positive : .negative) + ) + + if displayBottomRow { + middle1LeftItemLayoutAndApply = makeMiddle1LeftItemLayout( + params.width, + item.presentationData, + compactNumericCountString(Int(stats.viewers.current)), + item.presentationData.strings.Stats_GroupViewers, + (viewersDelta.text, viewersDelta.positive ? .positive : .negative) + ) + + middle1RightItemLayoutAndApply = makeMiddle1RightItemLayout( + params.width, + item.presentationData, + compactNumericCountString(Int(stats.posters.current)), + item.presentationData.strings.Stats_GroupPosters, + (postersDelta.text, postersDelta.positive ? .positive : .negative) + ) } - if twoColumnLayout { - if !stats.viewers.current.isZero || viewersDelta.2 || !stats.posters.current.isZero || postersDelta.2 { - height += verticalSpacing - height += bottomRightValueLabelLayoutAndApply!.0.size.height + bottomRightTitleLabelLayoutAndApply!.0.size.height - } + if twoColumnLayout || !displayBottomRow { + height += topRightItemLayoutAndApply!.0.height * 2.0 + verticalSpacing } else { - height += verticalSpacing - height += topRightValueLabelLayoutAndApply!.0.size.height + topRightTitleLabelLayoutAndApply!.0.size.height - if !stats.viewers.current.isZero || viewersDelta.2 { - height += verticalSpacing - height += bottomLeftValueLabelLayoutAndApply!.0.size.height + bottomLeftTitleLabelLayoutAndApply!.0.size.height - } - if !stats.posters.current.isZero || postersDelta.2 { - height += verticalSpacing - height += bottomRightValueLabelLayoutAndApply!.0.size.height + bottomRightTitleLabelLayoutAndApply!.0.size.height - } + height += topLeftItemLayoutAndApply!.0.height * 4.0 + verticalSpacing * 3.0 } - } else { - topLeftValueLabelLayoutAndApply = nil - topRightValueLabelLayoutAndApply = nil - bottomLeftValueLabelLayoutAndApply = nil - bottomRightValueLabelLayoutAndApply = nil - topLeftTitleLabelLayoutAndApply = nil - topRightTitleLabelLayoutAndApply = nil - bottomLeftTitleLabelLayoutAndApply = nil - bottomRightTitleLabelLayoutAndApply = nil - topLeftDeltaLabelLayoutAndApply = nil - topRightDeltaLabelLayoutAndApply = nil - bottomLeftDeltaLabelLayoutAndApply = nil - bottomRightDeltaLabelLayoutAndApply = nil } let contentSize = CGSize(width: params.width, height: height) @@ -405,18 +626,14 @@ class StatsOverviewItemNode: ListViewItemNode { if let strongSelf = self { strongSelf.item = item - let _ = topLeftValueLabelLayoutAndApply?.1() - let _ = topRightValueLabelLayoutAndApply?.1() - let _ = bottomLeftValueLabelLayoutAndApply?.1() - let _ = bottomRightValueLabelLayoutAndApply?.1() - let _ = topLeftTitleLabelLayoutAndApply?.1() - let _ = topRightTitleLabelLayoutAndApply?.1() - let _ = bottomLeftTitleLabelLayoutAndApply?.1() - let _ = bottomRightTitleLabelLayoutAndApply?.1() - let _ = topLeftDeltaLabelLayoutAndApply?.1() - let _ = topRightDeltaLabelLayoutAndApply?.1() - let _ = bottomLeftDeltaLabelLayoutAndApply?.1() - let _ = bottomRightDeltaLabelLayoutAndApply?.1() + let _ = topLeftItemLayoutAndApply?.1() + let _ = topRightItemLayoutAndApply?.1() + let _ = middle1LeftItemLayoutAndApply?.1() + let _ = middle1RightItemLayoutAndApply?.1() + let _ = middle2LeftItemLayoutAndApply?.1() + let _ = middle2RightItemLayoutAndApply?.1() + let _ = bottomLeftItemLayoutAndApply?.1() + let _ = bottomRightItemLayoutAndApply?.1() if let _ = updatedTheme { strongSelf.topStripeNode.backgroundColor = itemSeparatorColor @@ -482,47 +699,63 @@ class StatsOverviewItemNode: ListViewItemNode { strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight)) } - var secondColumnX = sideInset + leftInset + let firstColumnX = sideInset + leftInset + var secondColumnX = firstColumnX - if let topLeftValueLabelLayout = topLeftValueLabelLayoutAndApply?.0, let topLeftTitleLabelLayout = topLeftTitleLabelLayoutAndApply?.0 { - strongSelf.topLeftValueLabel.frame = CGRect(origin: CGPoint(x: sideInset + leftInset, y: topInset), size: topLeftValueLabelLayout.size) - strongSelf.topLeftTitleLabel.frame = CGRect(origin: CGPoint(x: sideInset + leftInset, y: strongSelf.topLeftValueLabel.frame.maxY), size: topLeftTitleLabelLayout.size) - - if twoColumnLayout { - let topWidth = topLeftValueLabelLayout.size.width + (topLeftDeltaLabelLayoutAndApply?.0.size.width ?? 0) - let bottomWidth = (bottomLeftValueLabelLayoutAndApply?.0.size.width ?? 0.0) + (bottomLeftDeltaLabelLayoutAndApply?.0.size.width ?? 0.0) - secondColumnX = max(layout.size.width / 2.0, sideInset + leftInset + max(topWidth, bottomWidth) + horizontalSpacing) + if twoColumnLayout { + var maxLeftWidth: CGFloat = 0.0 + if let topLeftItemLayout = topLeftItemLayoutAndApply?.0 { + maxLeftWidth = max(maxLeftWidth, topLeftItemLayout.width) } - } - if let topLeftDeltaLabelLayout = topLeftDeltaLabelLayoutAndApply?.0 { - strongSelf.topLeftDeltaLabel.frame = CGRect(origin: CGPoint(x: strongSelf.topLeftValueLabel.frame.maxX + horizontalSpacing, y: strongSelf.topLeftValueLabel.frame.maxY - topLeftDeltaLabelLayout.size.height - 2.0), size: topLeftDeltaLabelLayout.size) + if let middle1LeftItemLayout = middle1LeftItemLayoutAndApply?.0 { + maxLeftWidth = max(maxLeftWidth, middle1LeftItemLayout.width) + } + if let middle2LeftItemLayout = middle2LeftItemLayoutAndApply?.0 { + maxLeftWidth = max(maxLeftWidth, middle2LeftItemLayout.width) + } + if let bottomLeftItemLayout = bottomLeftItemLayoutAndApply?.0 { + maxLeftWidth = max(maxLeftWidth, bottomLeftItemLayout.width) + } + secondColumnX = max(layout.size.width / 2.0, firstColumnX + maxLeftWidth + horizontalSpacing) } - if let topRightValueLabelLayout = topRightValueLabelLayoutAndApply?.0, let topRightTitleLabelLayout = topRightTitleLabelLayoutAndApply?.0 { - let topRightY = twoColumnLayout ? topInset : strongSelf.topLeftTitleLabel.frame.maxY + verticalSpacing - strongSelf.topRightValueLabel.frame = CGRect(origin: CGPoint(x: secondColumnX, y: topRightY), size: topRightValueLabelLayout.size) - strongSelf.topRightTitleLabel.frame = CGRect(origin: CGPoint(x: secondColumnX, y: strongSelf.topRightValueLabel.frame.maxY), size: topRightTitleLabelLayout.size) + if let topLeftItemLayout = topLeftItemLayoutAndApply?.0 { + strongSelf.topLeftItem.frame = CGRect(origin: CGPoint(x: firstColumnX, y: topInset), size: topLeftItemLayout) + } + + if let topRightItemLayout = topRightItemLayoutAndApply?.0 { + let originY = twoColumnLayout ? topInset : strongSelf.topLeftItem.frame.maxY + verticalSpacing + strongSelf.topRightItem.frame = CGRect(origin: CGPoint(x: secondColumnX, y: originY), size: topRightItemLayout) + } + + if let middle1LeftItemLayout = middle1LeftItemLayoutAndApply?.0 { + let originY = (twoColumnLayout ? strongSelf.topLeftItem.frame.maxY : strongSelf.topRightItem.frame.maxY) + verticalSpacing + strongSelf.middle1LeftItem.frame = CGRect(origin: CGPoint(x: firstColumnX, y: originY), size: middle1LeftItemLayout) } - if let topRightDeltaLabelLayout = topRightDeltaLabelLayoutAndApply?.0 { - strongSelf.topRightDeltaLabel.frame = CGRect(origin: CGPoint(x: strongSelf.topRightValueLabel.frame.maxX + horizontalSpacing, y: strongSelf.topRightValueLabel.frame.maxY - topRightDeltaLabelLayout.size.height - 2.0), size: topRightDeltaLabelLayout.size) + + if let middle1RightItemLayout = middle1RightItemLayoutAndApply?.0 { + let originY = (twoColumnLayout ? strongSelf.topRightItem.frame.maxY : strongSelf.middle1LeftItem.frame.maxY) + verticalSpacing + strongSelf.middle1RightItem.frame = CGRect(origin: CGPoint(x: secondColumnX, y: originY), size: middle1RightItemLayout) } - if let bottomLeftValueLabelLayout = bottomLeftValueLabelLayoutAndApply?.0, let bottomLeftTitleLabelLayout = bottomLeftTitleLabelLayoutAndApply?.0 { - let bottomLeftY = twoColumnLayout ? strongSelf.topLeftTitleLabel.frame.maxY + verticalSpacing : strongSelf.topRightTitleLabel.frame.maxY + verticalSpacing - strongSelf.bottomLeftValueLabel.frame = CGRect(origin: CGPoint(x: sideInset + leftInset, y: bottomLeftY), size: bottomLeftValueLabelLayout.size) - strongSelf.bottomLeftTitleLabel.frame = CGRect(origin: CGPoint(x: sideInset + leftInset, y: strongSelf.bottomLeftValueLabel.frame.maxY), size: bottomLeftTitleLabelLayout.size) + if let middle2LeftItemLayout = middle2LeftItemLayoutAndApply?.0 { + let originY = (twoColumnLayout ? strongSelf.middle1LeftItem.frame.maxY : strongSelf.middle1RightItem.frame.maxY) + verticalSpacing + strongSelf.middle2LeftItem.frame = CGRect(origin: CGPoint(x: firstColumnX, y: originY), size: middle2LeftItemLayout) } - if let bottomLeftDeltaLabelLayout = bottomLeftDeltaLabelLayoutAndApply?.0 { - strongSelf.bottomLeftDeltaLabel.frame = CGRect(origin: CGPoint(x: strongSelf.bottomLeftValueLabel.frame.maxX + horizontalSpacing, y: strongSelf.bottomLeftValueLabel.frame.maxY - bottomLeftDeltaLabelLayout.size.height - 2.0), size: bottomLeftDeltaLabelLayout.size) + + if let middle2RightItemLayout = middle2RightItemLayoutAndApply?.0 { + let originY = (twoColumnLayout ? strongSelf.middle1RightItem.frame.maxY : strongSelf.middle2LeftItem.frame.maxY) + verticalSpacing + strongSelf.middle2RightItem.frame = CGRect(origin: CGPoint(x: secondColumnX, y: originY), size: middle2RightItemLayout) } - if let bottomRightValueLabelLayout = bottomRightValueLabelLayoutAndApply?.0, let bottomRightTitleLabelLayout = bottomRightTitleLabelLayoutAndApply?.0 { - let bottomRightY = twoColumnLayout ? strongSelf.topRightTitleLabel.frame.maxY + verticalSpacing : strongSelf.bottomLeftTitleLabel.frame.maxY + verticalSpacing - strongSelf.bottomRightValueLabel.frame = CGRect(origin: CGPoint(x: secondColumnX, y: bottomRightY), size: bottomRightValueLabelLayout.size) - strongSelf.bottomRightTitleLabel.frame = CGRect(origin: CGPoint(x: secondColumnX, y: strongSelf.bottomRightValueLabel.frame.maxY), size: bottomRightTitleLabelLayout.size) + if let bottomLeftItemLayout = bottomLeftItemLayoutAndApply?.0 { + let originY = (twoColumnLayout ? strongSelf.middle2LeftItem.frame.maxY : strongSelf.middle2RightItem.frame.maxY) + verticalSpacing + strongSelf.bottomLeftItem.frame = CGRect(origin: CGPoint(x: firstColumnX, y: originY), size: bottomLeftItemLayout) } - if let bottomRightDeltaLabelLayout = bottomRightDeltaLabelLayoutAndApply?.0 { - strongSelf.bottomRightDeltaLabel.frame = CGRect(origin: CGPoint(x: strongSelf.bottomRightValueLabel.frame.maxX + horizontalSpacing, y: strongSelf.bottomRightValueLabel.frame.maxY - bottomRightDeltaLabelLayout.size.height - 2.0), size: bottomRightDeltaLabelLayout.size) + + if let bottomRightItemLayout = bottomRightItemLayoutAndApply?.0 { + let originY = (twoColumnLayout ? strongSelf.middle2RightItem.frame.maxY : strongSelf.bottomLeftItem.frame.maxY) + verticalSpacing + strongSelf.bottomRightItem.frame = CGRect(origin: CGPoint(x: secondColumnX, y: originY), size: bottomRightItemLayout) } } }) diff --git a/submodules/StatisticsUI/Sources/StoryIconNode.swift b/submodules/StatisticsUI/Sources/StoryIconNode.swift new file mode 100644 index 00000000000..de2f823d41d --- /dev/null +++ b/submodules/StatisticsUI/Sources/StoryIconNode.swift @@ -0,0 +1,106 @@ +import Foundation +import Display +import AsyncDisplayKit +import ComponentFlow +import SwiftSignalKit +import Postbox +import TelegramCore +import PhotoResources +import AvatarStoryIndicatorComponent +import AccountContext +import TelegramPresentationData + +final class StoryIconNode: ASDisplayNode { + private let imageNode = TransformImageNode() + private let storyIndicator = ComponentView() + + init(context: AccountContext, theme: PresentationTheme, peer: Peer, storyItem: EngineStoryItem) { + self.imageNode.displaysAsynchronously = false + + super.init() + + self.addSubnode(self.imageNode) + + let imageSize = CGSize(width: 30.0, height: 30.0) + let size = CGSize(width: 36.0, height: 36.0) + let bounds = CGRect(origin: .zero, size: size) + + self.imageNode.frame = bounds.insetBy(dx: 3.0, dy: 3.0) + self.frame = bounds + + let media: Media? + switch storyItem.media { + case let .image(image): + media = image + case let .file(file): + media = file + default: + media = nil + } + + var updateImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? + var dimensions: CGSize? + if let peerReference = PeerReference(peer), let media { + if let image = media as? TelegramMediaImage { + updateImageSignal = mediaGridMessagePhoto(account: context.account, userLocation: .peer(peer.id), photoReference: .story(peer: peerReference, id: storyItem.id, media: image)) + dimensions = largestRepresentationForPhoto(image)?.dimensions.cgSize + } else if let file = media as? TelegramMediaFile { + updateImageSignal = mediaGridMessageVideo(postbox: context.account.postbox, userLocation: .peer(peer.id), videoReference: .story(peer: peerReference, id: storyItem.id, media: file), autoFetchFullSizeThumbnail: true) + dimensions = file.dimensions?.cgSize + } + } + + if let updateImageSignal { + self.imageNode.setSignal(updateImageSignal) + } + + if let dimensions { + let cornerRadius = imageSize.width / 2.0 + let makeImageLayout = self.imageNode.asyncLayout() + let applyImageLayout = makeImageLayout(TransformImageArguments(corners: ImageCorners(radius: cornerRadius), imageSize: dimensions.aspectFilled(imageSize), boundingSize: imageSize, intrinsicInsets: UIEdgeInsets())) + applyImageLayout() + } + + let lineWidth: CGFloat = 1.5 + let indicatorSize = CGSize(width: size.width - lineWidth * 4.0, height: size.height - lineWidth * 4.0) + + let _ = self.storyIndicator.update( + transition: .immediate, + component: AnyComponent(AvatarStoryIndicatorComponent( + hasUnseen: true, + hasUnseenCloseFriendsItems: false, + colors: AvatarStoryIndicatorComponent.Colors( + unseenColors: theme.chatList.storyUnseenColors.array, + unseenCloseFriendsColors: theme.chatList.storyUnseenPrivateColors.array, + seenColors: theme.chatList.storySeenColors.array + ), + activeLineWidth: lineWidth, + inactiveLineWidth: lineWidth, + counters: AvatarStoryIndicatorComponent.Counters( + totalCount: 1, + unseenCount: 1 + ), + progress: nil + )), + environment: {}, + containerSize: indicatorSize + ) + if let storyIndicatorView = self.storyIndicator.view { + storyIndicatorView.frame = CGRect(origin: CGPoint(x: bounds.midX - indicatorSize.width / 2.0, y: bounds.midY - indicatorSize.height / 2.0), size: indicatorSize) + } + } + + override func didLoad() { + super.didLoad() + + if let storyIndicatorView = self.storyIndicator.view { + if storyIndicatorView.superview == nil { + self.view.addSubview(storyIndicatorView) + } + } + } + + override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { + return CGSize(width: 36.0, height: 36.0) + } +} diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index cbb6492fe04..212ceef5d5f 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -166,7 +166,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-531931925] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsMentions($0) } dict[-566281095] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsRecent($0) } dict[106343499] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsSearch($0) } - dict[427944574] = { return Api.Chat.parse_channel($0) } + dict[-1903702824] = { return Api.Chat.parse_channel($0) } dict[399807445] = { return Api.Chat.parse_channelForbidden($0) } dict[1103884886] = { return Api.Chat.parse_chat($0) } dict[693512293] = { return Api.Chat.parse_chatEmpty($0) } @@ -393,7 +393,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-380694650] = { return Api.InputPrivacyRule.parse_inputPrivacyValueDisallowChatParticipants($0) } dict[195371015] = { return Api.InputPrivacyRule.parse_inputPrivacyValueDisallowContacts($0) } dict[-1877932953] = { return Api.InputPrivacyRule.parse_inputPrivacyValueDisallowUsers($0) } - dict[121554949] = { return Api.InputReplyTo.parse_inputReplyToMessage($0) } + dict[583071445] = { return Api.InputReplyTo.parse_inputReplyToMessage($0) } dict[363917955] = { return Api.InputReplyTo.parse_inputReplyToStory($0) } dict[1399317950] = { return Api.InputSecureFile.parse_inputSecureFile($0) } dict[859091184] = { return Api.InputSecureFile.parse_inputSecureFileUploaded($0) } @@ -490,6 +490,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-758129906] = { return Api.MessageAction.parse_messageActionGiftCode($0) } dict[-935499028] = { return Api.MessageAction.parse_messageActionGiftPremium($0) } dict[858499565] = { return Api.MessageAction.parse_messageActionGiveawayLaunch($0) } + dict[715107781] = { return Api.MessageAction.parse_messageActionGiveawayResults($0) } dict[2047704898] = { return Api.MessageAction.parse_messageActionGroupCall($0) } dict[-1281329567] = { return Api.MessageAction.parse_messageActionGroupCallScheduled($0) } dict[-1615153660] = { return Api.MessageAction.parse_messageActionHistoryClear($0) } @@ -503,9 +504,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { 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[-1136350937] = { return Api.MessageAction.parse_messageActionSetChatWallPaper($0) } + dict[1348510708] = { return Api.MessageAction.parse_messageActionSetChatWallPaper($0) } dict[1007897979] = { return Api.MessageAction.parse_messageActionSetMessagesTTL($0) } - dict[-1065845395] = { return Api.MessageAction.parse_messageActionSetSameChatWallPaper($0) } dict[1474192222] = { return Api.MessageAction.parse_messageActionSuggestProfilePhoto($0) } dict[228168278] = { return Api.MessageAction.parse_messageActionTopicCreate($0) } dict[-1064024032] = { return Api.MessageAction.parse_messageActionTopicEdit($0) } @@ -535,7 +535,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-297296796] = { return Api.MessageExtendedMedia.parse_messageExtendedMedia($0) } dict[-1386050360] = { return Api.MessageExtendedMedia.parse_messageExtendedMediaPreview($0) } dict[1601666510] = { return Api.MessageFwdHeader.parse_messageFwdHeader($0) } - dict[-1387279939] = { return Api.MessageInteractionCounters.parse_messageInteractionCounters($0) } dict[1882335561] = { return Api.MessageMedia.parse_messageMediaContact($0) } dict[1065280907] = { return Api.MessageMedia.parse_messageMediaDice($0) } dict[1291114285] = { return Api.MessageMedia.parse_messageMediaDocument($0) } @@ -558,7 +557,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[182649427] = { return Api.MessageRange.parse_messageRange($0) } dict[1328256121] = { return Api.MessageReactions.parse_messageReactions($0) } dict[-2083123262] = { return Api.MessageReplies.parse_messageReplies($0) } - dict[1860946621] = { return Api.MessageReplyHeader.parse_messageReplyHeader($0) } + dict[-1346631205] = { return Api.MessageReplyHeader.parse_messageReplyHeader($0) } dict[-1667711039] = { return Api.MessageReplyHeader.parse_messageReplyStoryHeader($0) } dict[1163625789] = { return Api.MessageViews.parse_messageViews($0) } dict[975236280] = { return Api.MessagesFilter.parse_inputMessagesFilterChatPhotos($0) } @@ -637,6 +636,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[918946202] = { return Api.Peer.parse_peerChat($0) } dict[1498486562] = { return Api.Peer.parse_peerUser($0) } dict[-386039788] = { return Api.PeerBlocked.parse_peerBlocked($0) } + dict[-1253352753] = { return Api.PeerColor.parse_peerColor($0) } dict[-901375139] = { return Api.PeerLocated.parse_peerLocated($0) } dict[-118740917] = { return Api.PeerLocated.parse_peerSelfLocated($0) } dict[-1721619444] = { return Api.PeerNotifySettings.parse_peerNotifySettings($0) } @@ -669,6 +669,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[2061444128] = { return Api.PollResults.parse_pollResults($0) } dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) } dict[512535275] = { return Api.PostAddress.parse_postAddress($0) } + dict[-419066241] = { return Api.PostInteractionCounters.parse_postInteractionCountersMessage($0) } + dict[-1974989273] = { return Api.PostInteractionCounters.parse_postInteractionCountersStory($0) } dict[629052971] = { return Api.PremiumGiftCodeOption.parse_premiumGiftCodeOption($0) } dict[1958953753] = { return Api.PremiumGiftOption.parse_premiumGiftOption($0) } dict[1596792306] = { return Api.PremiumSubscriptionOption.parse_premiumSubscriptionOption($0) } @@ -692,6 +694,8 @@ 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[32685898] = { return Api.PublicForward.parse_publicForwardMessage($0) } + dict[-302797360] = { return Api.PublicForward.parse_publicForwardStory($0) } dict[-1992950669] = { return Api.Reaction.parse_reactionCustomEmoji($0) } dict[455247544] = { return Api.Reaction.parse_reactionEmoji($0) } dict[2046153753] = { return Api.Reaction.parse_reactionEmpty($0) } @@ -797,7 +801,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-651419003] = { return Api.SendMessageAction.parse_speakingInGroupCallAction($0) } dict[-1239335713] = { return Api.ShippingOption.parse_shippingOption($0) } dict[-2010155333] = { return Api.SimpleWebViewResult.parse_simpleWebViewResultUrl($0) } - dict[-626000021] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) } + dict[-313293833] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) } dict[1035529315] = { return Api.SponsoredWebPage.parse_sponsoredWebPage($0) } dict[-884757282] = { return Api.StatsAbsValueAndPrev.parse_statsAbsValueAndPrev($0) } dict[-1237848657] = { return Api.StatsDateRangeDays.parse_statsDateRangeDays($0) } @@ -817,7 +821,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[872932635] = { return Api.StickerSetCovered.parse_stickerSetMultiCovered($0) } dict[2008112412] = { return Api.StickerSetCovered.parse_stickerSetNoCovered($0) } dict[1898850301] = { return Api.StoriesStealthMode.parse_storiesStealthMode($0) } - dict[1153718222] = { return Api.StoryItem.parse_storyItem($0) } + dict[-1205411504] = { return Api.StoryFwdHeader.parse_storyFwdHeader($0) } + dict[-1352440415] = { return Api.StoryItem.parse_storyItem($0) } dict[1374088783] = { return Api.StoryItem.parse_storyItemDeleted($0) } dict[-5388013] = { return Api.StoryItem.parse_storyItemSkipped($0) } dict[-1329730875] = { return Api.StoryView.parse_storyView($0) } @@ -838,6 +843,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[397910539] = { return Api.Update.parse_updateAttachMenuBots($0) } dict[-335171433] = { return Api.Update.parse_updateAutoSaveSettings($0) } dict[-1177566067] = { return Api.Update.parse_updateBotCallbackQuery($0) } + dict[-1873947492] = { return Api.Update.parse_updateBotChatBoost($0) } dict[299870598] = { return Api.Update.parse_updateBotChatInviteRequester($0) } dict[1299263278] = { return Api.Update.parse_updateBotCommands($0) } dict[1232025500] = { return Api.Update.parse_updateBotInlineQuery($0) } @@ -858,6 +864,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-366410403] = { return Api.Update.parse_updateChannelReadMessagesContents($0) } dict[277713951] = { return Api.Update.parse_updateChannelTooLong($0) } dict[-1937192669] = { return Api.Update.parse_updateChannelUserTyping($0) } + dict[129403168] = { return Api.Update.parse_updateChannelViewForumAsMessages($0) } dict[791390623] = { return Api.Update.parse_updateChannelWebPage($0) } dict[-124097970] = { return Api.Update.parse_updateChat($0) } dict[1421875280] = { return Api.Update.parse_updateChatDefaultBannedRights($0) } @@ -912,6 +919,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1147422299] = { return Api.Update.parse_updatePeerHistoryTTL($0) } dict[-1263546448] = { return Api.Update.parse_updatePeerLocated($0) } dict[1786671974] = { return Api.Update.parse_updatePeerSettings($0) } + dict[-1371598819] = { return Api.Update.parse_updatePeerWallpaper($0) } dict[1885586395] = { return Api.Update.parse_updatePendingJoinRequests($0) } dict[-1425052898] = { return Api.Update.parse_updatePhoneCall($0) } dict[643940105] = { return Api.Update.parse_updatePhoneCallSignalingData($0) } @@ -962,7 +970,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1886646706] = { return Api.UrlAuthResult.parse_urlAuthResultAccepted($0) } dict[-1445536993] = { return Api.UrlAuthResult.parse_urlAuthResultDefault($0) } dict[-1831650802] = { return Api.UrlAuthResult.parse_urlAuthResultRequest($0) } - dict[-346018011] = { return Api.User.parse_user($0) } + dict[559694904] = { return Api.User.parse_user($0) } dict[-742634630] = { return Api.User.parse_userEmpty($0) } dict[-1179571092] = { return Api.UserFull.parse_userFull($0) } dict[-2100168954] = { return Api.UserProfilePhoto.parse_userProfilePhoto($0) } @@ -1077,6 +1085,11 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[415997816] = { return Api.help.InviteText.parse_inviteText($0) } dict[-1600596305] = { return Api.help.PassportConfig.parse_passportConfig($0) } dict[-1078332329] = { return Api.help.PassportConfig.parse_passportConfigNotModified($0) } + dict[324785199] = { return Api.help.PeerColorOption.parse_peerColorOption($0) } + dict[1987928555] = { return Api.help.PeerColorSet.parse_peerColorProfileSet($0) } + dict[639736408] = { return Api.help.PeerColorSet.parse_peerColorSet($0) } + dict[16313608] = { return Api.help.PeerColors.parse_peerColors($0) } + dict[732034510] = { return Api.help.PeerColors.parse_peerColorsNotModified($0) } dict[1395946908] = { return Api.help.PremiumPromo.parse_premiumPromo($0) } dict[-1942390465] = { return Api.help.PromoData.parse_promoData($0) } dict[-1728664459] = { return Api.help.PromoData.parse_promoDataEmpty($0) } @@ -1155,7 +1168,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[946083368] = { return Api.messages.StickerSetInstallResult.parse_stickerSetInstallResultSuccess($0) } dict[816245886] = { return Api.messages.Stickers.parse_stickers($0) } dict[-244016606] = { return Api.messages.Stickers.parse_stickersNotModified($0) } - dict[-1821037486] = { return Api.messages.TranscribedAudio.parse_transcribedAudio($0) } + dict[-809903785] = { return Api.messages.TranscribedAudio.parse_transcribedAudio($0) } 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) } @@ -1183,9 +1196,11 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-2030542532] = { return Api.premium.BoostsList.parse_boostsList($0) } dict[1230586490] = { return Api.premium.BoostsStatus.parse_boostsStatus($0) } dict[-1696454430] = { return Api.premium.MyBoosts.parse_myBoosts($0) } - dict[-1107852396] = { return Api.stats.BroadcastStats.parse_broadcastStats($0) } + dict[963421692] = { return Api.stats.BroadcastStats.parse_broadcastStats($0) } dict[-276825834] = { return Api.stats.MegagroupStats.parse_megagroupStats($0) } - dict[-1986399595] = { return Api.stats.MessageStats.parse_messageStats($0) } + dict[2145983508] = { return Api.stats.MessageStats.parse_messageStats($0) } + dict[-1828487648] = { return Api.stats.PublicForwards.parse_publicForwards($0) } + dict[1355613820] = { return Api.stats.StoryStats.parse_storyStats($0) } dict[-2046910401] = { return Api.stickers.SuggestedShortName.parse_suggestedShortName($0) } dict[-891180321] = { return Api.storage.FileType.parse_fileGif($0) } dict[8322574] = { return Api.storage.FileType.parse_fileJpeg($0) } @@ -1588,8 +1603,6 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.MessageFwdHeader: _1.serialize(buffer, boxed) - case let _1 as Api.MessageInteractionCounters: - _1.serialize(buffer, boxed) case let _1 as Api.MessageMedia: _1.serialize(buffer, boxed) case let _1 as Api.MessagePeerReaction: @@ -1646,6 +1659,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.PeerBlocked: _1.serialize(buffer, boxed) + case let _1 as Api.PeerColor: + _1.serialize(buffer, boxed) case let _1 as Api.PeerLocated: _1.serialize(buffer, boxed) case let _1 as Api.PeerNotifySettings: @@ -1678,6 +1693,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.PostAddress: _1.serialize(buffer, boxed) + case let _1 as Api.PostInteractionCounters: + _1.serialize(buffer, boxed) case let _1 as Api.PremiumGiftCodeOption: _1.serialize(buffer, boxed) case let _1 as Api.PremiumGiftOption: @@ -1690,6 +1707,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.PrivacyRule: _1.serialize(buffer, boxed) + case let _1 as Api.PublicForward: + _1.serialize(buffer, boxed) case let _1 as Api.Reaction: _1.serialize(buffer, boxed) case let _1 as Api.ReactionCount: @@ -1776,6 +1795,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.StoriesStealthMode: _1.serialize(buffer, boxed) + case let _1 as Api.StoryFwdHeader: + _1.serialize(buffer, boxed) case let _1 as Api.StoryItem: _1.serialize(buffer, boxed) case let _1 as Api.StoryView: @@ -1930,6 +1951,12 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.help.PassportConfig: _1.serialize(buffer, boxed) + case let _1 as Api.help.PeerColorOption: + _1.serialize(buffer, boxed) + case let _1 as Api.help.PeerColorSet: + _1.serialize(buffer, boxed) + case let _1 as Api.help.PeerColors: + _1.serialize(buffer, boxed) case let _1 as Api.help.PremiumPromo: _1.serialize(buffer, boxed) case let _1 as Api.help.PromoData: @@ -2092,6 +2119,10 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.stats.MessageStats: _1.serialize(buffer, boxed) + case let _1 as Api.stats.PublicForwards: + _1.serialize(buffer, boxed) + case let _1 as Api.stats.StoryStats: + _1.serialize(buffer, boxed) case let _1 as Api.stickers.SuggestedShortName: _1.serialize(buffer, boxed) case let _1 as Api.storage.FileType: diff --git a/submodules/TelegramApi/Sources/Api10.swift b/submodules/TelegramApi/Sources/Api10.swift index 2402ce132c7..242e60ab2bf 100644 --- a/submodules/TelegramApi/Sources/Api10.swift +++ b/submodules/TelegramApi/Sources/Api10.swift @@ -1,13 +1,13 @@ public extension Api { indirect enum InputReplyTo: TypeConstructorDescription { - case inputReplyToMessage(flags: Int32, replyToMsgId: Int32, topMsgId: Int32?, replyToPeerId: Api.InputPeer?, quoteText: String?, quoteEntities: [Api.MessageEntity]?) + case inputReplyToMessage(flags: Int32, replyToMsgId: Int32, topMsgId: Int32?, replyToPeerId: Api.InputPeer?, quoteText: String?, quoteEntities: [Api.MessageEntity]?, quoteOffset: Int32?) case inputReplyToStory(userId: Api.InputUser, storyId: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .inputReplyToMessage(let flags, let replyToMsgId, let topMsgId, let replyToPeerId, let quoteText, let quoteEntities): + case .inputReplyToMessage(let flags, let replyToMsgId, let topMsgId, let replyToPeerId, let quoteText, let quoteEntities, let quoteOffset): if boxed { - buffer.appendInt32(121554949) + buffer.appendInt32(583071445) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(replyToMsgId, buffer: buffer, boxed: false) @@ -19,6 +19,7 @@ public extension Api { for item in quoteEntities! { item.serialize(buffer, true) }} + if Int(flags) & Int(1 << 4) != 0 {serializeInt32(quoteOffset!, buffer: buffer, boxed: false)} break case .inputReplyToStory(let userId, let storyId): if boxed { @@ -32,8 +33,8 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .inputReplyToMessage(let flags, let replyToMsgId, let topMsgId, let replyToPeerId, let quoteText, let quoteEntities): - return ("inputReplyToMessage", [("flags", flags as Any), ("replyToMsgId", replyToMsgId as Any), ("topMsgId", topMsgId as Any), ("replyToPeerId", replyToPeerId as Any), ("quoteText", quoteText as Any), ("quoteEntities", quoteEntities as Any)]) + case .inputReplyToMessage(let flags, let replyToMsgId, let topMsgId, let replyToPeerId, let quoteText, let quoteEntities, let quoteOffset): + return ("inputReplyToMessage", [("flags", flags as Any), ("replyToMsgId", replyToMsgId as Any), ("topMsgId", topMsgId as Any), ("replyToPeerId", replyToPeerId as Any), ("quoteText", quoteText as Any), ("quoteEntities", quoteEntities as Any), ("quoteOffset", quoteOffset as Any)]) case .inputReplyToStory(let userId, let storyId): return ("inputReplyToStory", [("userId", userId as Any), ("storyId", storyId as Any)]) } @@ -56,14 +57,17 @@ public extension Api { if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() { _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) } } + var _7: Int32? + if Int(_1!) & Int(1 << 4) != 0 {_7 = reader.readInt32() } 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 - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.InputReplyTo.inputReplyToMessage(flags: _1!, replyToMsgId: _2!, topMsgId: _3, replyToPeerId: _4, quoteText: _5, quoteEntities: _6) + let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.InputReplyTo.inputReplyToMessage(flags: _1!, replyToMsgId: _2!, topMsgId: _3, replyToPeerId: _4, quoteText: _5, quoteEntities: _6, quoteOffset: _7) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api12.swift b/submodules/TelegramApi/Sources/Api12.swift index 4b6509524cc..54a2a17dbac 100644 --- a/submodules/TelegramApi/Sources/Api12.swift +++ b/submodules/TelegramApi/Sources/Api12.swift @@ -612,6 +612,7 @@ public extension Api { case messageActionGiftCode(flags: Int32, boostPeer: Api.Peer?, months: Int32, slug: String) case messageActionGiftPremium(flags: Int32, currency: String, amount: Int64, months: Int32, cryptoCurrency: String?, cryptoAmount: Int64?) case messageActionGiveawayLaunch + case messageActionGiveawayResults(winnersCount: Int32, unclaimedCount: Int32) case messageActionGroupCall(flags: Int32, call: Api.InputGroupCall, duration: Int32?) case messageActionGroupCallScheduled(call: Api.InputGroupCall, scheduleDate: Int32) case messageActionHistoryClear @@ -625,9 +626,8 @@ public extension Api { case messageActionSecureValuesSent(types: [Api.SecureValueType]) case messageActionSecureValuesSentMe(values: [Api.SecureValue], credentials: Api.SecureCredentialsEncrypted) case messageActionSetChatTheme(emoticon: String) - case messageActionSetChatWallPaper(wallpaper: Api.WallPaper) + case messageActionSetChatWallPaper(flags: Int32, wallpaper: Api.WallPaper) case messageActionSetMessagesTTL(flags: Int32, period: Int32, autoSettingFrom: Int64?) - case messageActionSetSameChatWallPaper(wallpaper: Api.WallPaper) case messageActionSuggestProfilePhoto(photo: Api.Photo) case messageActionTopicCreate(flags: Int32, title: String, iconColor: Int32, iconEmojiId: Int64?) case messageActionTopicEdit(flags: Int32, title: String?, iconEmojiId: Int64?, closed: Api.Bool?, hidden: Api.Bool?) @@ -778,6 +778,13 @@ public extension Api { buffer.appendInt32(858499565) } + break + case .messageActionGiveawayResults(let winnersCount, let unclaimedCount): + if boxed { + buffer.appendInt32(715107781) + } + serializeInt32(winnersCount, buffer: buffer, boxed: false) + serializeInt32(unclaimedCount, buffer: buffer, boxed: false) break case .messageActionGroupCall(let flags, let call, let duration): if boxed { @@ -887,10 +894,11 @@ public extension Api { } serializeString(emoticon, buffer: buffer, boxed: false) break - case .messageActionSetChatWallPaper(let wallpaper): + case .messageActionSetChatWallPaper(let flags, let wallpaper): if boxed { - buffer.appendInt32(-1136350937) + buffer.appendInt32(1348510708) } + serializeInt32(flags, buffer: buffer, boxed: false) wallpaper.serialize(buffer, true) break case .messageActionSetMessagesTTL(let flags, let period, let autoSettingFrom): @@ -901,12 +909,6 @@ public extension Api { serializeInt32(period, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 0) != 0 {serializeInt64(autoSettingFrom!, buffer: buffer, boxed: false)} break - case .messageActionSetSameChatWallPaper(let wallpaper): - if boxed { - buffer.appendInt32(-1065845395) - } - wallpaper.serialize(buffer, true) - break case .messageActionSuggestProfilePhoto(let photo): if boxed { buffer.appendInt32(1474192222) @@ -990,6 +992,8 @@ public extension Api { return ("messageActionGiftPremium", [("flags", flags as Any), ("currency", currency as Any), ("amount", amount as Any), ("months", months as Any), ("cryptoCurrency", cryptoCurrency as Any), ("cryptoAmount", cryptoAmount as Any)]) case .messageActionGiveawayLaunch: return ("messageActionGiveawayLaunch", []) + case .messageActionGiveawayResults(let winnersCount, let unclaimedCount): + return ("messageActionGiveawayResults", [("winnersCount", winnersCount as Any), ("unclaimedCount", unclaimedCount as Any)]) case .messageActionGroupCall(let flags, let call, let duration): return ("messageActionGroupCall", [("flags", flags as Any), ("call", call as Any), ("duration", duration as Any)]) case .messageActionGroupCallScheduled(let call, let scheduleDate): @@ -1016,12 +1020,10 @@ public extension Api { return ("messageActionSecureValuesSentMe", [("values", values as Any), ("credentials", credentials as Any)]) case .messageActionSetChatTheme(let emoticon): return ("messageActionSetChatTheme", [("emoticon", emoticon as Any)]) - case .messageActionSetChatWallPaper(let wallpaper): - return ("messageActionSetChatWallPaper", [("wallpaper", wallpaper 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 .messageActionSetSameChatWallPaper(let wallpaper): - return ("messageActionSetSameChatWallPaper", [("wallpaper", wallpaper as Any)]) case .messageActionSuggestProfilePhoto(let photo): return ("messageActionSuggestProfilePhoto", [("photo", photo as Any)]) case .messageActionTopicCreate(let flags, let title, let iconColor, let iconEmojiId): @@ -1274,6 +1276,20 @@ public extension Api { public static func parse_messageActionGiveawayLaunch(_ reader: BufferReader) -> MessageAction? { return Api.MessageAction.messageActionGiveawayLaunch } + public static func parse_messageActionGiveawayResults(_ reader: BufferReader) -> MessageAction? { + 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.MessageAction.messageActionGiveawayResults(winnersCount: _1!, unclaimedCount: _2!) + } + else { + return nil + } + } public static func parse_messageActionGroupCall(_ reader: BufferReader) -> MessageAction? { var _1: Int32? _1 = reader.readInt32() @@ -1470,13 +1486,16 @@ public extension Api { } } public static func parse_messageActionSetChatWallPaper(_ reader: BufferReader) -> MessageAction? { - var _1: Api.WallPaper? + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.WallPaper? if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.WallPaper + _2 = Api.parse(reader, signature: signature) as? Api.WallPaper } let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionSetChatWallPaper(wallpaper: _1!) + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.MessageAction.messageActionSetChatWallPaper(flags: _1!, wallpaper: _2!) } else { return nil @@ -1499,19 +1518,6 @@ public extension Api { return nil } } - public static func parse_messageActionSetSameChatWallPaper(_ reader: BufferReader) -> MessageAction? { - var _1: Api.WallPaper? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.WallPaper - } - let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionSetSameChatWallPaper(wallpaper: _1!) - } - else { - return nil - } - } public static func parse_messageActionSuggestProfilePhoto(_ reader: BufferReader) -> MessageAction? { var _1: Api.Photo? if let signature = reader.readInt32() { diff --git a/submodules/TelegramApi/Sources/Api13.swift b/submodules/TelegramApi/Sources/Api13.swift index bfeed9804ab..6ff56823602 100644 --- a/submodules/TelegramApi/Sources/Api13.swift +++ b/submodules/TelegramApi/Sources/Api13.swift @@ -688,50 +688,6 @@ public extension Api { } } -public extension Api { - enum MessageInteractionCounters: TypeConstructorDescription { - case messageInteractionCounters(msgId: Int32, views: Int32, forwards: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .messageInteractionCounters(let msgId, let views, let forwards): - if boxed { - buffer.appendInt32(-1387279939) - } - serializeInt32(msgId, buffer: buffer, boxed: false) - serializeInt32(views, buffer: buffer, boxed: false) - serializeInt32(forwards, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .messageInteractionCounters(let msgId, let views, let forwards): - return ("messageInteractionCounters", [("msgId", msgId as Any), ("views", views as Any), ("forwards", forwards as Any)]) - } - } - - public static func parse_messageInteractionCounters(_ reader: BufferReader) -> MessageInteractionCounters? { - var _1: Int32? - _1 = reader.readInt32() - 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.MessageInteractionCounters.messageInteractionCounters(msgId: _1!, views: _2!, forwards: _3!) - } - else { - return nil - } - } - - } -} public extension Api { indirect enum MessageMedia: TypeConstructorDescription { case messageMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String, userId: Int64) diff --git a/submodules/TelegramApi/Sources/Api14.swift b/submodules/TelegramApi/Sources/Api14.swift index 6ccc340764b..9d7878ffecc 100644 --- a/submodules/TelegramApi/Sources/Api14.swift +++ b/submodules/TelegramApi/Sources/Api14.swift @@ -322,14 +322,14 @@ public extension Api { } public extension Api { indirect enum MessageReplyHeader: TypeConstructorDescription { - case messageReplyHeader(flags: Int32, replyToMsgId: Int32?, replyToPeerId: Api.Peer?, replyFrom: Api.MessageFwdHeader?, replyMedia: Api.MessageMedia?, replyToTopId: Int32?, quoteText: String?, quoteEntities: [Api.MessageEntity]?) + case messageReplyHeader(flags: Int32, replyToMsgId: Int32?, replyToPeerId: Api.Peer?, replyFrom: Api.MessageFwdHeader?, replyMedia: Api.MessageMedia?, replyToTopId: Int32?, quoteText: String?, quoteEntities: [Api.MessageEntity]?, quoteOffset: Int32?) case messageReplyStoryHeader(userId: Int64, storyId: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .messageReplyHeader(let flags, let replyToMsgId, let replyToPeerId, let replyFrom, let replyMedia, let replyToTopId, let quoteText, let quoteEntities): + case .messageReplyHeader(let flags, let replyToMsgId, let replyToPeerId, let replyFrom, let replyMedia, let replyToTopId, let quoteText, let quoteEntities, let quoteOffset): if boxed { - buffer.appendInt32(1860946621) + buffer.appendInt32(-1346631205) } serializeInt32(flags, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 4) != 0 {serializeInt32(replyToMsgId!, buffer: buffer, boxed: false)} @@ -343,6 +343,7 @@ public extension Api { for item in quoteEntities! { item.serialize(buffer, true) }} + if Int(flags) & Int(1 << 10) != 0 {serializeInt32(quoteOffset!, buffer: buffer, boxed: false)} break case .messageReplyStoryHeader(let userId, let storyId): if boxed { @@ -356,8 +357,8 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .messageReplyHeader(let flags, let replyToMsgId, let replyToPeerId, let replyFrom, let replyMedia, let replyToTopId, let quoteText, let quoteEntities): - return ("messageReplyHeader", [("flags", flags as Any), ("replyToMsgId", replyToMsgId as Any), ("replyToPeerId", replyToPeerId as Any), ("replyFrom", replyFrom as Any), ("replyMedia", replyMedia as Any), ("replyToTopId", replyToTopId as Any), ("quoteText", quoteText as Any), ("quoteEntities", quoteEntities as Any)]) + case .messageReplyHeader(let flags, let replyToMsgId, let replyToPeerId, let replyFrom, let replyMedia, let replyToTopId, let quoteText, let quoteEntities, let quoteOffset): + return ("messageReplyHeader", [("flags", flags as Any), ("replyToMsgId", replyToMsgId as Any), ("replyToPeerId", replyToPeerId as Any), ("replyFrom", replyFrom as Any), ("replyMedia", replyMedia as Any), ("replyToTopId", replyToTopId as Any), ("quoteText", quoteText as Any), ("quoteEntities", quoteEntities as Any), ("quoteOffset", quoteOffset as Any)]) case .messageReplyStoryHeader(let userId, let storyId): return ("messageReplyStoryHeader", [("userId", userId as Any), ("storyId", storyId as Any)]) } @@ -388,6 +389,8 @@ public extension Api { if Int(_1!) & Int(1 << 7) != 0 {if let _ = reader.readInt32() { _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) } } + var _9: Int32? + if Int(_1!) & Int(1 << 10) != 0 {_9 = reader.readInt32() } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 4) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil @@ -396,8 +399,9 @@ public extension Api { 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 << 7) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.MessageReplyHeader.messageReplyHeader(flags: _1!, replyToMsgId: _2, replyToPeerId: _3, replyFrom: _4, replyMedia: _5, replyToTopId: _6, quoteText: _7, quoteEntities: _8) + let _c9 = (Int(_1!) & Int(1 << 10) == 0) || _9 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return Api.MessageReplyHeader.messageReplyHeader(flags: _1!, replyToMsgId: _2, replyToPeerId: _3, replyFrom: _4, replyMedia: _5, replyToTopId: _6, quoteText: _7, quoteEntities: _8, quoteOffset: _9) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api16.swift b/submodules/TelegramApi/Sources/Api16.swift index 8992670e635..258955a62f2 100644 --- a/submodules/TelegramApi/Sources/Api16.swift +++ b/submodules/TelegramApi/Sources/Api16.swift @@ -686,6 +686,50 @@ public extension Api { } } +public extension Api { + enum PeerColor: TypeConstructorDescription { + case peerColor(flags: Int32, color: Int32?, backgroundEmojiId: Int64?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .peerColor(let flags, let color, let backgroundEmojiId): + if boxed { + buffer.appendInt32(-1253352753) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(color!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt64(backgroundEmojiId!, buffer: buffer, boxed: false)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .peerColor(let flags, let color, let backgroundEmojiId): + return ("peerColor", [("flags", flags as Any), ("color", color as Any), ("backgroundEmojiId", backgroundEmojiId as Any)]) + } + } + + public static func parse_peerColor(_ reader: BufferReader) -> PeerColor? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } + var _3: Int64? + if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt64() } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.PeerColor.peerColor(flags: _1!, color: _2, backgroundEmojiId: _3) + } + else { + return nil + } + } + + } +} public extension Api { enum PeerLocated: TypeConstructorDescription { case peerLocated(peer: Api.Peer, expires: Int32, distance: Int32) diff --git a/submodules/TelegramApi/Sources/Api17.swift b/submodules/TelegramApi/Sources/Api17.swift index ef702e0207c..eb8088c6ecc 100644 --- a/submodules/TelegramApi/Sources/Api17.swift +++ b/submodules/TelegramApi/Sources/Api17.swift @@ -750,6 +750,86 @@ public extension Api { } } +public extension Api { + enum PostInteractionCounters: TypeConstructorDescription { + case postInteractionCountersMessage(msgId: Int32, views: Int32, forwards: Int32, reactions: Int32) + case postInteractionCountersStory(storyId: Int32, views: Int32, forwards: Int32, reactions: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .postInteractionCountersMessage(let msgId, let views, let forwards, let reactions): + if boxed { + buffer.appendInt32(-419066241) + } + serializeInt32(msgId, buffer: buffer, boxed: false) + serializeInt32(views, buffer: buffer, boxed: false) + serializeInt32(forwards, buffer: buffer, boxed: false) + serializeInt32(reactions, buffer: buffer, boxed: false) + break + case .postInteractionCountersStory(let storyId, let views, let forwards, let reactions): + if boxed { + buffer.appendInt32(-1974989273) + } + serializeInt32(storyId, buffer: buffer, boxed: false) + serializeInt32(views, buffer: buffer, boxed: false) + serializeInt32(forwards, buffer: buffer, boxed: false) + serializeInt32(reactions, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .postInteractionCountersMessage(let msgId, let views, let forwards, let reactions): + return ("postInteractionCountersMessage", [("msgId", msgId as Any), ("views", views as Any), ("forwards", forwards as Any), ("reactions", reactions as Any)]) + case .postInteractionCountersStory(let storyId, let views, let forwards, let reactions): + return ("postInteractionCountersStory", [("storyId", storyId as Any), ("views", views as Any), ("forwards", forwards as Any), ("reactions", reactions as Any)]) + } + } + + public static func parse_postInteractionCountersMessage(_ reader: BufferReader) -> PostInteractionCounters? { + var _1: Int32? + _1 = reader.readInt32() + 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.PostInteractionCounters.postInteractionCountersMessage(msgId: _1!, views: _2!, forwards: _3!, reactions: _4!) + } + else { + return nil + } + } + public static func parse_postInteractionCountersStory(_ reader: BufferReader) -> PostInteractionCounters? { + var _1: Int32? + _1 = reader.readInt32() + 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.PostInteractionCounters.postInteractionCountersStory(storyId: _1!, views: _2!, forwards: _3!, reactions: _4!) + } + else { + return nil + } + } + + } +} public extension Api { enum PremiumGiftCodeOption: TypeConstructorDescription { case premiumGiftCodeOption(flags: Int32, users: Int32, months: Int32, storeProduct: String?, storeQuantity: Int32?, currency: String, amount: Int64) @@ -1110,183 +1190,3 @@ public extension Api { } } -public extension Api { - enum PrivacyRule: TypeConstructorDescription { - case privacyValueAllowAll - case privacyValueAllowChatParticipants(chats: [Int64]) - case privacyValueAllowCloseFriends - case privacyValueAllowContacts - case privacyValueAllowUsers(users: [Int64]) - case privacyValueDisallowAll - case privacyValueDisallowChatParticipants(chats: [Int64]) - case privacyValueDisallowContacts - case privacyValueDisallowUsers(users: [Int64]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .privacyValueAllowAll: - if boxed { - buffer.appendInt32(1698855810) - } - - break - case .privacyValueAllowChatParticipants(let chats): - if boxed { - buffer.appendInt32(1796427406) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - serializeInt64(item, buffer: buffer, boxed: false) - } - break - case .privacyValueAllowCloseFriends: - if boxed { - buffer.appendInt32(-135735141) - } - - break - case .privacyValueAllowContacts: - if boxed { - buffer.appendInt32(-123988) - } - - break - case .privacyValueAllowUsers(let users): - if boxed { - buffer.appendInt32(-1198497870) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - serializeInt64(item, buffer: buffer, boxed: false) - } - break - case .privacyValueDisallowAll: - if boxed { - buffer.appendInt32(-1955338397) - } - - break - case .privacyValueDisallowChatParticipants(let chats): - if boxed { - buffer.appendInt32(1103656293) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - serializeInt64(item, buffer: buffer, boxed: false) - } - break - case .privacyValueDisallowContacts: - if boxed { - buffer.appendInt32(-125240806) - } - - break - case .privacyValueDisallowUsers(let users): - if boxed { - buffer.appendInt32(-463335103) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - serializeInt64(item, buffer: buffer, boxed: false) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .privacyValueAllowAll: - return ("privacyValueAllowAll", []) - case .privacyValueAllowChatParticipants(let chats): - return ("privacyValueAllowChatParticipants", [("chats", chats as Any)]) - case .privacyValueAllowCloseFriends: - return ("privacyValueAllowCloseFriends", []) - case .privacyValueAllowContacts: - return ("privacyValueAllowContacts", []) - case .privacyValueAllowUsers(let users): - return ("privacyValueAllowUsers", [("users", users as Any)]) - case .privacyValueDisallowAll: - return ("privacyValueDisallowAll", []) - case .privacyValueDisallowChatParticipants(let chats): - return ("privacyValueDisallowChatParticipants", [("chats", chats as Any)]) - case .privacyValueDisallowContacts: - return ("privacyValueDisallowContacts", []) - case .privacyValueDisallowUsers(let users): - return ("privacyValueDisallowUsers", [("users", users as Any)]) - } - } - - public static func parse_privacyValueAllowAll(_ reader: BufferReader) -> PrivacyRule? { - return Api.PrivacyRule.privacyValueAllowAll - } - public static func parse_privacyValueAllowChatParticipants(_ reader: BufferReader) -> PrivacyRule? { - var _1: [Int64]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) - } - let _c1 = _1 != nil - if _c1 { - return Api.PrivacyRule.privacyValueAllowChatParticipants(chats: _1!) - } - else { - return nil - } - } - public static func parse_privacyValueAllowCloseFriends(_ reader: BufferReader) -> PrivacyRule? { - return Api.PrivacyRule.privacyValueAllowCloseFriends - } - public static func parse_privacyValueAllowContacts(_ reader: BufferReader) -> PrivacyRule? { - return Api.PrivacyRule.privacyValueAllowContacts - } - public static func parse_privacyValueAllowUsers(_ reader: BufferReader) -> PrivacyRule? { - var _1: [Int64]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) - } - let _c1 = _1 != nil - if _c1 { - return Api.PrivacyRule.privacyValueAllowUsers(users: _1!) - } - else { - return nil - } - } - public static func parse_privacyValueDisallowAll(_ reader: BufferReader) -> PrivacyRule? { - return Api.PrivacyRule.privacyValueDisallowAll - } - public static func parse_privacyValueDisallowChatParticipants(_ reader: BufferReader) -> PrivacyRule? { - var _1: [Int64]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) - } - let _c1 = _1 != nil - if _c1 { - return Api.PrivacyRule.privacyValueDisallowChatParticipants(chats: _1!) - } - else { - return nil - } - } - public static func parse_privacyValueDisallowContacts(_ reader: BufferReader) -> PrivacyRule? { - return Api.PrivacyRule.privacyValueDisallowContacts - } - public static func parse_privacyValueDisallowUsers(_ reader: BufferReader) -> PrivacyRule? { - var _1: [Int64]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) - } - let _c1 = _1 != nil - if _c1 { - return Api.PrivacyRule.privacyValueDisallowUsers(users: _1!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api18.swift b/submodules/TelegramApi/Sources/Api18.swift index a314943e1ae..cce8c834d79 100644 --- a/submodules/TelegramApi/Sources/Api18.swift +++ b/submodules/TelegramApi/Sources/Api18.swift @@ -1,3 +1,249 @@ +public extension Api { + enum PrivacyRule: TypeConstructorDescription { + case privacyValueAllowAll + case privacyValueAllowChatParticipants(chats: [Int64]) + case privacyValueAllowCloseFriends + case privacyValueAllowContacts + case privacyValueAllowUsers(users: [Int64]) + case privacyValueDisallowAll + case privacyValueDisallowChatParticipants(chats: [Int64]) + case privacyValueDisallowContacts + case privacyValueDisallowUsers(users: [Int64]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .privacyValueAllowAll: + if boxed { + buffer.appendInt32(1698855810) + } + + break + case .privacyValueAllowChatParticipants(let chats): + if boxed { + buffer.appendInt32(1796427406) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + serializeInt64(item, buffer: buffer, boxed: false) + } + break + case .privacyValueAllowCloseFriends: + if boxed { + buffer.appendInt32(-135735141) + } + + break + case .privacyValueAllowContacts: + if boxed { + buffer.appendInt32(-123988) + } + + break + case .privacyValueAllowUsers(let users): + if boxed { + buffer.appendInt32(-1198497870) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + serializeInt64(item, buffer: buffer, boxed: false) + } + break + case .privacyValueDisallowAll: + if boxed { + buffer.appendInt32(-1955338397) + } + + break + case .privacyValueDisallowChatParticipants(let chats): + if boxed { + buffer.appendInt32(1103656293) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + serializeInt64(item, buffer: buffer, boxed: false) + } + break + case .privacyValueDisallowContacts: + if boxed { + buffer.appendInt32(-125240806) + } + + break + case .privacyValueDisallowUsers(let users): + if boxed { + buffer.appendInt32(-463335103) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + serializeInt64(item, buffer: buffer, boxed: false) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .privacyValueAllowAll: + return ("privacyValueAllowAll", []) + case .privacyValueAllowChatParticipants(let chats): + return ("privacyValueAllowChatParticipants", [("chats", chats as Any)]) + case .privacyValueAllowCloseFriends: + return ("privacyValueAllowCloseFriends", []) + case .privacyValueAllowContacts: + return ("privacyValueAllowContacts", []) + case .privacyValueAllowUsers(let users): + return ("privacyValueAllowUsers", [("users", users as Any)]) + case .privacyValueDisallowAll: + return ("privacyValueDisallowAll", []) + case .privacyValueDisallowChatParticipants(let chats): + return ("privacyValueDisallowChatParticipants", [("chats", chats as Any)]) + case .privacyValueDisallowContacts: + return ("privacyValueDisallowContacts", []) + case .privacyValueDisallowUsers(let users): + return ("privacyValueDisallowUsers", [("users", users as Any)]) + } + } + + public static func parse_privacyValueAllowAll(_ reader: BufferReader) -> PrivacyRule? { + return Api.PrivacyRule.privacyValueAllowAll + } + public static func parse_privacyValueAllowChatParticipants(_ reader: BufferReader) -> PrivacyRule? { + var _1: [Int64]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.PrivacyRule.privacyValueAllowChatParticipants(chats: _1!) + } + else { + return nil + } + } + public static func parse_privacyValueAllowCloseFriends(_ reader: BufferReader) -> PrivacyRule? { + return Api.PrivacyRule.privacyValueAllowCloseFriends + } + public static func parse_privacyValueAllowContacts(_ reader: BufferReader) -> PrivacyRule? { + return Api.PrivacyRule.privacyValueAllowContacts + } + public static func parse_privacyValueAllowUsers(_ reader: BufferReader) -> PrivacyRule? { + var _1: [Int64]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.PrivacyRule.privacyValueAllowUsers(users: _1!) + } + else { + return nil + } + } + public static func parse_privacyValueDisallowAll(_ reader: BufferReader) -> PrivacyRule? { + return Api.PrivacyRule.privacyValueDisallowAll + } + public static func parse_privacyValueDisallowChatParticipants(_ reader: BufferReader) -> PrivacyRule? { + var _1: [Int64]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.PrivacyRule.privacyValueDisallowChatParticipants(chats: _1!) + } + else { + return nil + } + } + public static func parse_privacyValueDisallowContacts(_ reader: BufferReader) -> PrivacyRule? { + return Api.PrivacyRule.privacyValueDisallowContacts + } + public static func parse_privacyValueDisallowUsers(_ reader: BufferReader) -> PrivacyRule? { + var _1: [Int64]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.PrivacyRule.privacyValueDisallowUsers(users: _1!) + } + else { + return nil + } + } + + } +} +public extension Api { + indirect enum PublicForward: TypeConstructorDescription { + case publicForwardMessage(message: Api.Message) + case publicForwardStory(peer: Api.Peer, story: Api.StoryItem) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .publicForwardMessage(let message): + if boxed { + buffer.appendInt32(32685898) + } + message.serialize(buffer, true) + break + case .publicForwardStory(let peer, let story): + if boxed { + buffer.appendInt32(-302797360) + } + peer.serialize(buffer, true) + story.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .publicForwardMessage(let message): + return ("publicForwardMessage", [("message", message as Any)]) + case .publicForwardStory(let peer, let story): + return ("publicForwardStory", [("peer", peer as Any), ("story", story as Any)]) + } + } + + public static func parse_publicForwardMessage(_ reader: BufferReader) -> PublicForward? { + var _1: Api.Message? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Message + } + let _c1 = _1 != nil + if _c1 { + return Api.PublicForward.publicForwardMessage(message: _1!) + } + else { + return nil + } + } + public static func parse_publicForwardStory(_ reader: BufferReader) -> PublicForward? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _2: Api.StoryItem? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.StoryItem + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.PublicForward.publicForwardStory(peer: _1!, story: _2!) + } + else { + return nil + } + } + + } +} public extension Api { enum Reaction: TypeConstructorDescription { case reactionCustomEmoji(documentId: Int64) @@ -588,563 +834,3 @@ public extension Api { } } -public extension Api { - enum RequestPeerType: TypeConstructorDescription { - case requestPeerTypeBroadcast(flags: Int32, hasUsername: Api.Bool?, userAdminRights: Api.ChatAdminRights?, botAdminRights: Api.ChatAdminRights?) - case requestPeerTypeChat(flags: Int32, hasUsername: Api.Bool?, forum: Api.Bool?, userAdminRights: Api.ChatAdminRights?, botAdminRights: Api.ChatAdminRights?) - case requestPeerTypeUser(flags: Int32, bot: Api.Bool?, premium: Api.Bool?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .requestPeerTypeBroadcast(let flags, let hasUsername, let userAdminRights, let botAdminRights): - if boxed { - buffer.appendInt32(865857388) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 3) != 0 {hasUsername!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {userAdminRights!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {botAdminRights!.serialize(buffer, true)} - break - case .requestPeerTypeChat(let flags, let hasUsername, let forum, let userAdminRights, let botAdminRights): - if boxed { - buffer.appendInt32(-906990053) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 3) != 0 {hasUsername!.serialize(buffer, true)} - if Int(flags) & Int(1 << 4) != 0 {forum!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {userAdminRights!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {botAdminRights!.serialize(buffer, true)} - break - case .requestPeerTypeUser(let flags, let bot, let premium): - if boxed { - buffer.appendInt32(1597737472) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {bot!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {premium!.serialize(buffer, true)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .requestPeerTypeBroadcast(let flags, let hasUsername, let userAdminRights, let botAdminRights): - return ("requestPeerTypeBroadcast", [("flags", flags as Any), ("hasUsername", hasUsername as Any), ("userAdminRights", userAdminRights as Any), ("botAdminRights", botAdminRights as Any)]) - case .requestPeerTypeChat(let flags, let hasUsername, let forum, let userAdminRights, let botAdminRights): - return ("requestPeerTypeChat", [("flags", flags as Any), ("hasUsername", hasUsername as Any), ("forum", forum as Any), ("userAdminRights", userAdminRights as Any), ("botAdminRights", botAdminRights as Any)]) - case .requestPeerTypeUser(let flags, let bot, let premium): - return ("requestPeerTypeUser", [("flags", flags as Any), ("bot", bot as Any), ("premium", premium as Any)]) - } - } - - public static func parse_requestPeerTypeBroadcast(_ reader: BufferReader) -> RequestPeerType? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.Bool? - if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.Bool - } } - var _3: Api.ChatAdminRights? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights - } } - var _4: Api.ChatAdminRights? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights - } } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 3) == 0) || _2 != nil - let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.RequestPeerType.requestPeerTypeBroadcast(flags: _1!, hasUsername: _2, userAdminRights: _3, botAdminRights: _4) - } - else { - return nil - } - } - public static func parse_requestPeerTypeChat(_ reader: BufferReader) -> RequestPeerType? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.Bool? - if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.Bool - } } - var _3: Api.Bool? - if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.Bool - } } - var _4: Api.ChatAdminRights? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights - } } - var _5: Api.ChatAdminRights? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights - } } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 3) == 0) || _2 != nil - let _c3 = (Int(_1!) & Int(1 << 4) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil - let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.RequestPeerType.requestPeerTypeChat(flags: _1!, hasUsername: _2, forum: _3, userAdminRights: _4, botAdminRights: _5) - } - else { - return nil - } - } - public static func parse_requestPeerTypeUser(_ reader: BufferReader) -> RequestPeerType? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.Bool? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.Bool - } } - var _3: Api.Bool? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.Bool - } } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.RequestPeerType.requestPeerTypeUser(flags: _1!, bot: _2, premium: _3) - } - else { - return nil - } - } - - } -} -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/Api19.swift b/submodules/TelegramApi/Sources/Api19.swift index 14c977bb4a2..3b54211e639 100644 --- a/submodules/TelegramApi/Sources/Api19.swift +++ b/submodules/TelegramApi/Sources/Api19.swift @@ -1,179 +1,125 @@ public extension Api { - enum SavedContact: TypeConstructorDescription { - case savedPhoneContact(phone: String, firstName: String, lastName: String, date: Int32) + enum RequestPeerType: TypeConstructorDescription { + case requestPeerTypeBroadcast(flags: Int32, hasUsername: Api.Bool?, userAdminRights: Api.ChatAdminRights?, botAdminRights: Api.ChatAdminRights?) + case requestPeerTypeChat(flags: Int32, hasUsername: Api.Bool?, forum: Api.Bool?, userAdminRights: Api.ChatAdminRights?, botAdminRights: Api.ChatAdminRights?) + case requestPeerTypeUser(flags: Int32, bot: Api.Bool?, premium: Api.Bool?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .savedPhoneContact(let phone, let firstName, let lastName, let date): + case .requestPeerTypeBroadcast(let flags, let hasUsername, let userAdminRights, let botAdminRights): if boxed { - buffer.appendInt32(289586518) + buffer.appendInt32(865857388) } - serializeString(phone, buffer: buffer, boxed: false) - serializeString(firstName, buffer: buffer, boxed: false) - serializeString(lastName, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 3) != 0 {hasUsername!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {userAdminRights!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {botAdminRights!.serialize(buffer, true)} break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .savedPhoneContact(let phone, let firstName, let lastName, let date): - return ("savedPhoneContact", [("phone", phone as Any), ("firstName", firstName as Any), ("lastName", lastName as Any), ("date", date as Any)]) - } - } - - public static func parse_savedPhoneContact(_ reader: BufferReader) -> SavedContact? { - var _1: String? - _1 = parseString(reader) - var _2: String? - _2 = parseString(reader) - var _3: String? - _3 = parseString(reader) - 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.SavedContact.savedPhoneContact(phone: _1!, firstName: _2!, lastName: _3!, date: _4!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum SearchResultsCalendarPeriod: TypeConstructorDescription { - case searchResultsCalendarPeriod(date: Int32, minMsgId: Int32, maxMsgId: Int32, count: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .searchResultsCalendarPeriod(let date, let minMsgId, let maxMsgId, let count): + case .requestPeerTypeChat(let flags, let hasUsername, let forum, let userAdminRights, let botAdminRights): if boxed { - buffer.appendInt32(-911191137) + buffer.appendInt32(-906990053) } - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt32(minMsgId, buffer: buffer, boxed: false) - serializeInt32(maxMsgId, buffer: buffer, boxed: false) - serializeInt32(count, buffer: buffer, boxed: false) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 3) != 0 {hasUsername!.serialize(buffer, true)} + if Int(flags) & Int(1 << 4) != 0 {forum!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {userAdminRights!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {botAdminRights!.serialize(buffer, true)} + break + case .requestPeerTypeUser(let flags, let bot, let premium): + if boxed { + buffer.appendInt32(1597737472) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {bot!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {premium!.serialize(buffer, true)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .searchResultsCalendarPeriod(let date, let minMsgId, let maxMsgId, let count): - return ("searchResultsCalendarPeriod", [("date", date as Any), ("minMsgId", minMsgId as Any), ("maxMsgId", maxMsgId as Any), ("count", count as Any)]) + case .requestPeerTypeBroadcast(let flags, let hasUsername, let userAdminRights, let botAdminRights): + return ("requestPeerTypeBroadcast", [("flags", flags as Any), ("hasUsername", hasUsername as Any), ("userAdminRights", userAdminRights as Any), ("botAdminRights", botAdminRights as Any)]) + case .requestPeerTypeChat(let flags, let hasUsername, let forum, let userAdminRights, let botAdminRights): + return ("requestPeerTypeChat", [("flags", flags as Any), ("hasUsername", hasUsername as Any), ("forum", forum as Any), ("userAdminRights", userAdminRights as Any), ("botAdminRights", botAdminRights as Any)]) + case .requestPeerTypeUser(let flags, let bot, let premium): + return ("requestPeerTypeUser", [("flags", flags as Any), ("bot", bot as Any), ("premium", premium as Any)]) } } - public static func parse_searchResultsCalendarPeriod(_ reader: BufferReader) -> SearchResultsCalendarPeriod? { + public static func parse_requestPeerTypeBroadcast(_ reader: BufferReader) -> RequestPeerType? { 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 _2: Api.Bool? + if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Bool + } } + var _3: Api.ChatAdminRights? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights + } } + var _4: Api.ChatAdminRights? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights + } } let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil + let _c2 = (Int(_1!) & Int(1 << 3) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil if _c1 && _c2 && _c3 && _c4 { - return Api.SearchResultsCalendarPeriod.searchResultsCalendarPeriod(date: _1!, minMsgId: _2!, maxMsgId: _3!, count: _4!) + return Api.RequestPeerType.requestPeerTypeBroadcast(flags: _1!, hasUsername: _2, userAdminRights: _3, botAdminRights: _4) } else { return nil } } - - } -} -public extension Api { - enum SearchResultsPosition: TypeConstructorDescription { - case searchResultPosition(msgId: Int32, date: Int32, offset: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .searchResultPosition(let msgId, let date, let offset): - if boxed { - buffer.appendInt32(2137295719) - } - serializeInt32(msgId, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt32(offset, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .searchResultPosition(let msgId, let date, let offset): - return ("searchResultPosition", [("msgId", msgId as Any), ("date", date as Any), ("offset", offset as Any)]) - } - } - - public static func parse_searchResultPosition(_ reader: BufferReader) -> SearchResultsPosition? { + public static func parse_requestPeerTypeChat(_ reader: BufferReader) -> RequestPeerType? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() + var _2: Api.Bool? + if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Bool + } } + var _3: Api.Bool? + if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.Bool + } } + var _4: Api.ChatAdminRights? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights + } } + var _5: Api.ChatAdminRights? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights + } } let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SearchResultsPosition.searchResultPosition(msgId: _1!, date: _2!, offset: _3!) + let _c2 = (Int(_1!) & Int(1 << 3) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 4) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.RequestPeerType.requestPeerTypeChat(flags: _1!, hasUsername: _2, forum: _3, userAdminRights: _4, botAdminRights: _5) } else { return nil } } - - } -} -public extension Api { - enum SecureCredentialsEncrypted: TypeConstructorDescription { - case secureCredentialsEncrypted(data: Buffer, hash: Buffer, secret: Buffer) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .secureCredentialsEncrypted(let data, let hash, let secret): - if boxed { - buffer.appendInt32(871426631) - } - serializeBytes(data, buffer: buffer, boxed: false) - serializeBytes(hash, buffer: buffer, boxed: false) - serializeBytes(secret, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .secureCredentialsEncrypted(let data, let hash, let secret): - return ("secureCredentialsEncrypted", [("data", data as Any), ("hash", hash as Any), ("secret", secret as Any)]) - } - } - - public static func parse_secureCredentialsEncrypted(_ reader: BufferReader) -> SecureCredentialsEncrypted? { - var _1: Buffer? - _1 = parseBytes(reader) - var _2: Buffer? - _2 = parseBytes(reader) - var _3: Buffer? - _3 = parseBytes(reader) + public static func parse_requestPeerTypeUser(_ reader: BufferReader) -> RequestPeerType? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Bool? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Bool + } } + var _3: Api.Bool? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.Bool + } } let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil if _c1 && _c2 && _c3 { - return Api.SecureCredentialsEncrypted.secureCredentialsEncrypted(data: _1!, hash: _2!, secret: _3!) + return Api.RequestPeerType.requestPeerTypeUser(flags: _1!, bot: _2, premium: _3) } else { return nil @@ -183,41 +129,41 @@ public extension Api { } } public extension Api { - enum SecureData: TypeConstructorDescription { - case secureData(data: Buffer, dataHash: Buffer, secret: Buffer) + enum RestrictionReason: TypeConstructorDescription { + case restrictionReason(platform: String, reason: String, text: String) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .secureData(let data, let dataHash, let secret): + case .restrictionReason(let platform, let reason, let text): if boxed { - buffer.appendInt32(-1964327229) + buffer.appendInt32(-797791052) } - serializeBytes(data, buffer: buffer, boxed: false) - serializeBytes(dataHash, buffer: buffer, boxed: false) - serializeBytes(secret, buffer: buffer, boxed: false) + 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 .secureData(let data, let dataHash, let secret): - return ("secureData", [("data", data as Any), ("dataHash", dataHash as Any), ("secret", secret as Any)]) + 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_secureData(_ reader: BufferReader) -> SecureData? { - var _1: Buffer? - _1 = parseBytes(reader) - var _2: Buffer? - _2 = parseBytes(reader) - var _3: Buffer? - _3 = parseBytes(reader) + 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.SecureData.secureData(data: _1!, dataHash: _2!, secret: _3!) + return Api.RestrictionReason.restrictionReason(platform: _1!, reason: _2!, text: _3!) } else { return nil @@ -227,705 +173,523 @@ public extension Api { } } public extension Api { - enum SecureFile: TypeConstructorDescription { - case secureFile(id: Int64, accessHash: Int64, size: Int64, dcId: Int32, date: Int32, fileHash: Buffer, secret: Buffer) - case secureFileEmpty + 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 .secureFile(let id, let accessHash, let size, let dcId, let date, let fileHash, let secret): + case .textAnchor(let text, let name): if boxed { - buffer.appendInt32(2097791614) + buffer.appendInt32(894777186) } - serializeInt64(id, buffer: buffer, boxed: false) - serializeInt64(accessHash, buffer: buffer, boxed: false) - serializeInt64(size, buffer: buffer, boxed: false) - serializeInt32(dcId, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeBytes(fileHash, buffer: buffer, boxed: false) - serializeBytes(secret, buffer: buffer, boxed: false) + text.serialize(buffer, true) + serializeString(name, buffer: buffer, boxed: false) break - case .secureFileEmpty: + case .textBold(let text): if boxed { - buffer.appendInt32(1679398724) + 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 .secureFile(let id, let accessHash, let size, let dcId, let date, let fileHash, let secret): - return ("secureFile", [("id", id as Any), ("accessHash", accessHash as Any), ("size", size as Any), ("dcId", dcId as Any), ("date", date as Any), ("fileHash", fileHash as Any), ("secret", secret as Any)]) - case .secureFileEmpty: - return ("secureFileEmpty", []) - } - } - - public static func parse_secureFile(_ reader: BufferReader) -> SecureFile? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int64? - _3 = reader.readInt64() - var _4: Int32? - _4 = reader.readInt32() - var _5: Int32? - _5 = reader.readInt32() - var _6: Buffer? - _6 = parseBytes(reader) - var _7: Buffer? - _7 = parseBytes(reader) + 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 - 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.SecureFile.secureFile(id: _1!, accessHash: _2!, size: _3!, dcId: _4!, date: _5!, fileHash: _6!, secret: _7!) + if _c1 && _c2 { + return Api.RichText.textAnchor(text: _1!, name: _2!) } else { return nil } } - public static func parse_secureFileEmpty(_ reader: BufferReader) -> SecureFile? { - return Api.SecureFile.secureFileEmpty - } - - } -} -public extension Api { - enum SecurePasswordKdfAlgo: TypeConstructorDescription { - case securePasswordKdfAlgoPBKDF2HMACSHA512iter100000(salt: Buffer) - case securePasswordKdfAlgoSHA512(salt: Buffer) - case securePasswordKdfAlgoUnknown - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .securePasswordKdfAlgoPBKDF2HMACSHA512iter100000(let salt): - if boxed { - buffer.appendInt32(-1141711456) - } - serializeBytes(salt, buffer: buffer, boxed: false) - break - case .securePasswordKdfAlgoSHA512(let salt): - if boxed { - buffer.appendInt32(-2042159726) - } - serializeBytes(salt, buffer: buffer, boxed: false) - break - case .securePasswordKdfAlgoUnknown: - if boxed { - buffer.appendInt32(4883767) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .securePasswordKdfAlgoPBKDF2HMACSHA512iter100000(let salt): - return ("securePasswordKdfAlgoPBKDF2HMACSHA512iter100000", [("salt", salt as Any)]) - case .securePasswordKdfAlgoSHA512(let salt): - return ("securePasswordKdfAlgoSHA512", [("salt", salt as Any)]) - case .securePasswordKdfAlgoUnknown: - return ("securePasswordKdfAlgoUnknown", []) - } - } - - public static func parse_securePasswordKdfAlgoPBKDF2HMACSHA512iter100000(_ reader: BufferReader) -> SecurePasswordKdfAlgo? { - var _1: Buffer? - _1 = parseBytes(reader) + 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.SecurePasswordKdfAlgo.securePasswordKdfAlgoPBKDF2HMACSHA512iter100000(salt: _1!) + return Api.RichText.textBold(text: _1!) } else { return nil } } - public static func parse_securePasswordKdfAlgoSHA512(_ reader: BufferReader) -> SecurePasswordKdfAlgo? { - var _1: Buffer? - _1 = parseBytes(reader) + 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.SecurePasswordKdfAlgo.securePasswordKdfAlgoSHA512(salt: _1!) + return Api.RichText.textConcat(texts: _1!) } else { return nil } } - public static func parse_securePasswordKdfAlgoUnknown(_ reader: BufferReader) -> SecurePasswordKdfAlgo? { - return Api.SecurePasswordKdfAlgo.securePasswordKdfAlgoUnknown - } - - } -} -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) + 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 - if _c1 { - return Api.SecurePlainData.securePlainEmail(email: _1!) + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.RichText.textEmail(text: _1!, email: _2!) } else { return nil } } - public static func parse_securePlainPhone(_ reader: BufferReader) -> SecurePlainData? { - var _1: String? - _1 = parseString(reader) + 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.SecurePlainData.securePlainPhone(phone: _1!) + return Api.RichText.textFixed(text: _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 - } + 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 - if _c1 && _c2 { - return Api.SecureRequiredType.secureRequiredType(flags: _1!, type: _2!) + 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_secureRequiredTypeOneOf(_ reader: BufferReader) -> SecureRequiredType? { - var _1: [Api.SecureRequiredType]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureRequiredType.self) + 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.SecureRequiredType.secureRequiredTypeOneOf(types: _1!) + return Api.RichText.textItalic(text: _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? + 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.SecurePasswordKdfAlgo + _1 = Api.parse(reader, signature: signature) as? Api.RichText } - 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!) + if _c1 { + return Api.RichText.textMarked(text: _1!) } 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? + public static func parse_textPhone(_ reader: BufferReader) -> RichText? { + var _1: Api.RichText? if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.SecureValueType + _1 = Api.parse(reader, signature: signature) as? Api.RichText } - 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) + var _2: String? + _2 = parseString(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!) + if _c1 && _c2 { + return Api.RichText.textPhone(text: _1!, phone: _2!) } 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) + public static func parse_textPlain(_ reader: BufferReader) -> RichText? { + var _1: String? + _1 = 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!) + if _c1 { + return Api.RichText.textPlain(text: _1!) } else { return nil } } - public static func parse_secureValueErrorData(_ reader: BufferReader) -> SecureValueError? { - var _1: Api.SecureValueType? + 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.SecureValueType + _1 = Api.parse(reader, signature: signature) as? Api.RichText } - 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!) + if _c1 { + return Api.RichText.textStrike(text: _1!) } else { return nil } } - public static func parse_secureValueErrorFile(_ reader: BufferReader) -> SecureValueError? { - var _1: Api.SecureValueType? + 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.SecureValueType + _1 = Api.parse(reader, signature: signature) as? Api.RichText } - 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!) + if _c1 { + return Api.RichText.textSubscript(text: _1!) } else { return nil } } - public static func parse_secureValueErrorFiles(_ reader: BufferReader) -> SecureValueError? { - var _1: Api.SecureValueType? + 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.SecureValueType - } - var _2: [Buffer]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: -1255641564, elementType: Buffer.self) + _1 = Api.parse(reader, signature: signature) as? Api.RichText } - 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!) + if _c1 { + return Api.RichText.textSuperscript(text: _1!) } else { return nil } } - public static func parse_secureValueErrorFrontSide(_ reader: BufferReader) -> SecureValueError? { - var _1: Api.SecureValueType? + 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.SecureValueType + _1 = Api.parse(reader, signature: signature) as? Api.RichText } - 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!) + if _c1 { + return Api.RichText.textUnderline(text: _1!) } else { return nil } } - public static func parse_secureValueErrorReverseSide(_ reader: BufferReader) -> SecureValueError? { - var _1: Api.SecureValueType? + 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.SecureValueType + _1 = Api.parse(reader, signature: signature) as? Api.RichText } - var _2: Buffer? - _2 = parseBytes(reader) - var _3: String? - _3 = parseString(reader) + 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.SecureValueError.secureValueErrorReverseSide(type: _1!, fileHash: _2!, text: _3!) + return Api.RichText.textUrl(text: _1!, url: _2!, webpageId: _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) + + } +} +public extension Api { + enum SavedContact: TypeConstructorDescription { + case savedPhoneContact(phone: String, firstName: String, lastName: String, date: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .savedPhoneContact(let phone, let firstName, let lastName, let date): + if boxed { + buffer.appendInt32(289586518) + } + serializeString(phone, buffer: buffer, boxed: false) + serializeString(firstName, buffer: buffer, boxed: false) + serializeString(lastName, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .savedPhoneContact(let phone, let firstName, let lastName, let date): + return ("savedPhoneContact", [("phone", phone as Any), ("firstName", firstName as Any), ("lastName", lastName as Any), ("date", date as Any)]) + } + } + + public static func parse_savedPhoneContact(_ reader: BufferReader) -> SavedContact? { + var _1: String? + _1 = parseString(reader) + var _2: String? + _2 = parseString(reader) var _3: String? _3 = parseString(reader) + var _4: Int32? + _4 = reader.readInt32() 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!) + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.SavedContact.savedPhoneContact(phone: _1!, firstName: _2!, lastName: _3!, date: _4!) } 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) + + } +} +public extension Api { + enum SearchResultsCalendarPeriod: TypeConstructorDescription { + case searchResultsCalendarPeriod(date: Int32, minMsgId: Int32, maxMsgId: Int32, count: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .searchResultsCalendarPeriod(let date, let minMsgId, let maxMsgId, let count): + if boxed { + buffer.appendInt32(-911191137) + } + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt32(minMsgId, buffer: buffer, boxed: false) + serializeInt32(maxMsgId, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .searchResultsCalendarPeriod(let date, let minMsgId, let maxMsgId, let count): + return ("searchResultsCalendarPeriod", [("date", date as Any), ("minMsgId", minMsgId as Any), ("maxMsgId", maxMsgId as Any), ("count", count as Any)]) + } + } + + public static func parse_searchResultsCalendarPeriod(_ reader: BufferReader) -> SearchResultsCalendarPeriod? { + var _1: Int32? + _1 = reader.readInt32() + 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 - if _c1 && _c2 && _c3 { - return Api.SecureValueError.secureValueErrorTranslationFile(type: _1!, fileHash: _2!, text: _3!) + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.SearchResultsCalendarPeriod.searchResultsCalendarPeriod(date: _1!, minMsgId: _2!, maxMsgId: _3!, count: _4!) } 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) + + } +} +public extension Api { + enum SearchResultsPosition: TypeConstructorDescription { + case searchResultPosition(msgId: Int32, date: Int32, offset: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .searchResultPosition(let msgId, let date, let offset): + if boxed { + buffer.appendInt32(2137295719) + } + serializeInt32(msgId, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt32(offset, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .searchResultPosition(let msgId, let date, let offset): + return ("searchResultPosition", [("msgId", msgId as Any), ("date", date as Any), ("offset", offset as Any)]) + } + } + + public static func parse_searchResultPosition(_ reader: BufferReader) -> SearchResultsPosition? { + var _1: Int32? + _1 = reader.readInt32() + 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.SecureValueError.secureValueErrorTranslationFiles(type: _1!, fileHash: _2!, text: _3!) + return Api.SearchResultsPosition.searchResultPosition(msgId: _1!, date: _2!, offset: _3!) } else { return nil @@ -935,39 +699,41 @@ public extension Api { } } public extension Api { - enum SecureValueHash: TypeConstructorDescription { - case secureValueHash(type: Api.SecureValueType, hash: Buffer) + enum SecureCredentialsEncrypted: TypeConstructorDescription { + case secureCredentialsEncrypted(data: Buffer, hash: Buffer, secret: Buffer) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .secureValueHash(let type, let hash): + case .secureCredentialsEncrypted(let data, let hash, let secret): if boxed { - buffer.appendInt32(-316748368) + buffer.appendInt32(871426631) } - type.serialize(buffer, true) + serializeBytes(data, buffer: buffer, boxed: false) serializeBytes(hash, buffer: buffer, boxed: false) + serializeBytes(secret, 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)]) + case .secureCredentialsEncrypted(let data, let hash, let secret): + return ("secureCredentialsEncrypted", [("data", data as Any), ("hash", hash as Any), ("secret", secret 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 - } + public static func parse_secureCredentialsEncrypted(_ reader: BufferReader) -> SecureCredentialsEncrypted? { + var _1: Buffer? + _1 = parseBytes(reader) var _2: Buffer? _2 = parseBytes(reader) + var _3: Buffer? + _3 = parseBytes(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.SecureValueHash.secureValueHash(type: _1!, hash: _2!) + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.SecureCredentialsEncrypted.secureCredentialsEncrypted(data: _1!, hash: _2!, secret: _3!) } else { return nil @@ -977,173 +743,241 @@ public extension Api { } } 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 + enum SecureData: TypeConstructorDescription { + case secureData(data: Buffer, dataHash: Buffer, secret: Buffer) 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: + case .secureData(let data, let dataHash, let secret): if boxed { - buffer.appendInt32(-1717268701) + buffer.appendInt32(-1964327229) } - + serializeBytes(data, buffer: buffer, boxed: false) + serializeBytes(dataHash, buffer: buffer, boxed: false) + serializeBytes(secret, buffer: buffer, boxed: false) break - case .secureValueTypePassport: + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .secureData(let data, let dataHash, let secret): + return ("secureData", [("data", data as Any), ("dataHash", dataHash as Any), ("secret", secret as Any)]) + } + } + + public static func parse_secureData(_ reader: BufferReader) -> SecureData? { + var _1: Buffer? + _1 = parseBytes(reader) + var _2: Buffer? + _2 = parseBytes(reader) + var _3: Buffer? + _3 = parseBytes(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.SecureData.secureData(data: _1!, dataHash: _2!, secret: _3!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum SecureFile: TypeConstructorDescription { + case secureFile(id: Int64, accessHash: Int64, size: Int64, dcId: Int32, date: Int32, fileHash: Buffer, secret: Buffer) + case secureFileEmpty + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .secureFile(let id, let accessHash, let size, let dcId, let date, let fileHash, let secret): if boxed { - buffer.appendInt32(1034709504) + buffer.appendInt32(2097791614) } - + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + serializeInt64(size, buffer: buffer, boxed: false) + serializeInt32(dcId, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeBytes(fileHash, buffer: buffer, boxed: false) + serializeBytes(secret, buffer: buffer, boxed: false) break - case .secureValueTypePassportRegistration: + case .secureFileEmpty: if boxed { - buffer.appendInt32(-1713143702) + buffer.appendInt32(1679398724) } break - case .secureValueTypePersonalDetails: + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .secureFile(let id, let accessHash, let size, let dcId, let date, let fileHash, let secret): + return ("secureFile", [("id", id as Any), ("accessHash", accessHash as Any), ("size", size as Any), ("dcId", dcId as Any), ("date", date as Any), ("fileHash", fileHash as Any), ("secret", secret as Any)]) + case .secureFileEmpty: + return ("secureFileEmpty", []) + } + } + + public static func parse_secureFile(_ reader: BufferReader) -> SecureFile? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int64? + _3 = reader.readInt64() + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() + var _6: Buffer? + _6 = parseBytes(reader) + var _7: Buffer? + _7 = parseBytes(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 = _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.SecureFile.secureFile(id: _1!, accessHash: _2!, size: _3!, dcId: _4!, date: _5!, fileHash: _6!, secret: _7!) + } + else { + return nil + } + } + public static func parse_secureFileEmpty(_ reader: BufferReader) -> SecureFile? { + return Api.SecureFile.secureFileEmpty + } + + } +} +public extension Api { + enum SecurePasswordKdfAlgo: TypeConstructorDescription { + case securePasswordKdfAlgoPBKDF2HMACSHA512iter100000(salt: Buffer) + case securePasswordKdfAlgoSHA512(salt: Buffer) + case securePasswordKdfAlgoUnknown + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .securePasswordKdfAlgoPBKDF2HMACSHA512iter100000(let salt): if boxed { - buffer.appendInt32(-1658158621) + buffer.appendInt32(-1141711456) } - + serializeBytes(salt, buffer: buffer, boxed: false) break - case .secureValueTypePhone: + case .securePasswordKdfAlgoSHA512(let salt): if boxed { - buffer.appendInt32(-1289704741) + buffer.appendInt32(-2042159726) } - + serializeBytes(salt, buffer: buffer, boxed: false) break - case .secureValueTypeRentalAgreement: + case .securePasswordKdfAlgoUnknown: if boxed { - buffer.appendInt32(-1954007928) + buffer.appendInt32(4883767) } break - case .secureValueTypeTemporaryRegistration: + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .securePasswordKdfAlgoPBKDF2HMACSHA512iter100000(let salt): + return ("securePasswordKdfAlgoPBKDF2HMACSHA512iter100000", [("salt", salt as Any)]) + case .securePasswordKdfAlgoSHA512(let salt): + return ("securePasswordKdfAlgoSHA512", [("salt", salt as Any)]) + case .securePasswordKdfAlgoUnknown: + return ("securePasswordKdfAlgoUnknown", []) + } + } + + public static func parse_securePasswordKdfAlgoPBKDF2HMACSHA512iter100000(_ reader: BufferReader) -> SecurePasswordKdfAlgo? { + var _1: Buffer? + _1 = parseBytes(reader) + let _c1 = _1 != nil + if _c1 { + return Api.SecurePasswordKdfAlgo.securePasswordKdfAlgoPBKDF2HMACSHA512iter100000(salt: _1!) + } + else { + return nil + } + } + public static func parse_securePasswordKdfAlgoSHA512(_ reader: BufferReader) -> SecurePasswordKdfAlgo? { + var _1: Buffer? + _1 = parseBytes(reader) + let _c1 = _1 != nil + if _c1 { + return Api.SecurePasswordKdfAlgo.securePasswordKdfAlgoSHA512(salt: _1!) + } + else { + return nil + } + } + public static func parse_securePasswordKdfAlgoUnknown(_ reader: BufferReader) -> SecurePasswordKdfAlgo? { + return Api.SecurePasswordKdfAlgo.securePasswordKdfAlgoUnknown + } + + } +} +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(-368907213) + buffer.appendInt32(569137759) } - + serializeString(email, buffer: buffer, boxed: false) break - case .secureValueTypeUtilityBill: + case .securePlainPhone(let phone): if boxed { - buffer.appendInt32(-63531698) + buffer.appendInt32(2103482845) } - + serializeString(phone, buffer: buffer, boxed: false) 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", []) + case .securePlainEmail(let email): + return ("securePlainEmail", [("email", email as Any)]) + case .securePlainPhone(let phone): + return ("securePlainPhone", [("phone", phone as Any)]) } } - 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_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_secureValueTypeUtilityBill(_ reader: BufferReader) -> SecureValueType? { - return Api.SecureValueType.secureValueTypeUtilityBill + public static func parse_securePlainPhone(_ reader: BufferReader) -> SecurePlainData? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.SecurePlainData.securePlainPhone(phone: _1!) + } + else { + return nil + } } } diff --git a/submodules/TelegramApi/Sources/Api20.swift b/submodules/TelegramApi/Sources/Api20.swift index 0e768dc594b..9100f367995 100644 --- a/submodules/TelegramApi/Sources/Api20.swift +++ b/submodules/TelegramApi/Sources/Api20.swift @@ -1,37 +1,63 @@ public extension Api { - enum SendAsPeer: TypeConstructorDescription { - case sendAsPeer(flags: Int32, peer: Api.Peer) + 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 .sendAsPeer(let flags, let peer): + case .secureRequiredType(let flags, let type): if boxed { - buffer.appendInt32(-1206095820) + buffer.appendInt32(-2103600678) } serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) + 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 .sendAsPeer(let flags, let peer): - return ("sendAsPeer", [("flags", flags as Any), ("peer", peer as Any)]) + 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_sendAsPeer(_ reader: BufferReader) -> SendAsPeer? { + public static func parse_secureRequiredType(_ reader: BufferReader) -> SecureRequiredType? { var _1: Int32? _1 = reader.readInt32() - var _2: Api.Peer? + var _2: Api.SecureValueType? if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.Peer + _2 = Api.parse(reader, signature: signature) as? Api.SecureValueType } let _c1 = _1 != nil let _c2 = _2 != nil if _c1 && _c2 { - return Api.SendAsPeer.sendAsPeer(flags: _1!, peer: _2!) + 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 @@ -41,353 +67,441 @@ public extension Api { } } public extension Api { - enum SendMessageAction: TypeConstructorDescription { - case sendMessageCancelAction - case sendMessageChooseContactAction - case sendMessageChooseStickerAction - case sendMessageEmojiInteraction(emoticon: String, msgId: Int32, interaction: Api.DataJSON) - case sendMessageEmojiInteractionSeen(emoticon: String) - case sendMessageGamePlayAction - case sendMessageGeoLocationAction - case sendMessageHistoryImportAction(progress: Int32) - case sendMessageRecordAudioAction - case sendMessageRecordRoundAction - case sendMessageRecordVideoAction - case sendMessageTypingAction - case sendMessageUploadAudioAction(progress: Int32) - case sendMessageUploadDocumentAction(progress: Int32) - case sendMessageUploadPhotoAction(progress: Int32) - case sendMessageUploadRoundAction(progress: Int32) - case sendMessageUploadVideoAction(progress: Int32) - case speakingInGroupCallAction + enum SecureSecretSettings: TypeConstructorDescription { + case secureSecretSettings(secureAlgo: Api.SecurePasswordKdfAlgo, secureSecret: Buffer, secureSecretId: Int64) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .sendMessageCancelAction: - if boxed { - buffer.appendInt32(-44119819) - } - - break - case .sendMessageChooseContactAction: - if boxed { - buffer.appendInt32(1653390447) - } - - break - case .sendMessageChooseStickerAction: - if boxed { - buffer.appendInt32(-1336228175) - } - - break - case .sendMessageEmojiInteraction(let emoticon, let msgId, let interaction): + case .secureSecretSettings(let secureAlgo, let secureSecret, let secureSecretId): if boxed { - buffer.appendInt32(630664139) - } - serializeString(emoticon, buffer: buffer, boxed: false) - serializeInt32(msgId, buffer: buffer, boxed: false) - interaction.serialize(buffer, true) - break - case .sendMessageEmojiInteractionSeen(let emoticon): - if boxed { - buffer.appendInt32(-1234857938) - } - serializeString(emoticon, buffer: buffer, boxed: false) - break - case .sendMessageGamePlayAction: - if boxed { - buffer.appendInt32(-580219064) + buffer.appendInt32(354925740) } - + secureAlgo.serialize(buffer, true) + serializeBytes(secureSecret, buffer: buffer, boxed: false) + serializeInt64(secureSecretId, buffer: buffer, boxed: false) break - case .sendMessageGeoLocationAction: + } + } + + 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(393186209) + 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 - case .sendMessageHistoryImportAction(let progress): + } + } + + 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(-606432698) + buffer.appendInt32(-2036501105) } - serializeInt32(progress, buffer: buffer, boxed: false) + type.serialize(buffer, true) + serializeBytes(hash, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) break - case .sendMessageRecordAudioAction: + case .secureValueErrorData(let type, let dataHash, let field, let text): if boxed { - buffer.appendInt32(-718310409) + 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 .sendMessageRecordRoundAction: + case .secureValueErrorFile(let type, let fileHash, let text): if boxed { - buffer.appendInt32(-1997373508) + buffer.appendInt32(2054162547) } - + type.serialize(buffer, true) + serializeBytes(fileHash, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) break - case .sendMessageRecordVideoAction: + case .secureValueErrorFiles(let type, let fileHash, let text): if boxed { - buffer.appendInt32(-1584933265) + buffer.appendInt32(1717706985) } - - break - case .sendMessageTypingAction: - if boxed { - buffer.appendInt32(381645902) + 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 .sendMessageUploadAudioAction(let progress): + case .secureValueErrorFrontSide(let type, let fileHash, let text): if boxed { - buffer.appendInt32(-212740181) + buffer.appendInt32(12467706) } - serializeInt32(progress, buffer: buffer, boxed: false) + type.serialize(buffer, true) + serializeBytes(fileHash, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) break - case .sendMessageUploadDocumentAction(let progress): + case .secureValueErrorReverseSide(let type, let fileHash, let text): if boxed { - buffer.appendInt32(-1441998364) + buffer.appendInt32(-2037765467) } - serializeInt32(progress, buffer: buffer, boxed: false) + type.serialize(buffer, true) + serializeBytes(fileHash, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) break - case .sendMessageUploadPhotoAction(let progress): + case .secureValueErrorSelfie(let type, let fileHash, let text): if boxed { - buffer.appendInt32(-774682074) + buffer.appendInt32(-449327402) } - serializeInt32(progress, buffer: buffer, boxed: false) + type.serialize(buffer, true) + serializeBytes(fileHash, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) break - case .sendMessageUploadRoundAction(let progress): + case .secureValueErrorTranslationFile(let type, let fileHash, let text): if boxed { - buffer.appendInt32(608050278) + buffer.appendInt32(-1592506512) } - serializeInt32(progress, buffer: buffer, boxed: false) + type.serialize(buffer, true) + serializeBytes(fileHash, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) break - case .sendMessageUploadVideoAction(let progress): + case .secureValueErrorTranslationFiles(let type, let fileHash, let text): if boxed { - buffer.appendInt32(-378127636) + buffer.appendInt32(878931416) } - serializeInt32(progress, buffer: buffer, boxed: false) - break - case .speakingInGroupCallAction: - if boxed { - buffer.appendInt32(-651419003) + 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 .sendMessageCancelAction: - return ("sendMessageCancelAction", []) - case .sendMessageChooseContactAction: - return ("sendMessageChooseContactAction", []) - case .sendMessageChooseStickerAction: - return ("sendMessageChooseStickerAction", []) - case .sendMessageEmojiInteraction(let emoticon, let msgId, let interaction): - return ("sendMessageEmojiInteraction", [("emoticon", emoticon as Any), ("msgId", msgId as Any), ("interaction", interaction as Any)]) - case .sendMessageEmojiInteractionSeen(let emoticon): - return ("sendMessageEmojiInteractionSeen", [("emoticon", emoticon as Any)]) - case .sendMessageGamePlayAction: - return ("sendMessageGamePlayAction", []) - case .sendMessageGeoLocationAction: - return ("sendMessageGeoLocationAction", []) - case .sendMessageHistoryImportAction(let progress): - return ("sendMessageHistoryImportAction", [("progress", progress as Any)]) - case .sendMessageRecordAudioAction: - return ("sendMessageRecordAudioAction", []) - case .sendMessageRecordRoundAction: - return ("sendMessageRecordRoundAction", []) - case .sendMessageRecordVideoAction: - return ("sendMessageRecordVideoAction", []) - case .sendMessageTypingAction: - return ("sendMessageTypingAction", []) - case .sendMessageUploadAudioAction(let progress): - return ("sendMessageUploadAudioAction", [("progress", progress as Any)]) - case .sendMessageUploadDocumentAction(let progress): - return ("sendMessageUploadDocumentAction", [("progress", progress as Any)]) - case .sendMessageUploadPhotoAction(let progress): - return ("sendMessageUploadPhotoAction", [("progress", progress as Any)]) - case .sendMessageUploadRoundAction(let progress): - return ("sendMessageUploadRoundAction", [("progress", progress as Any)]) - case .sendMessageUploadVideoAction(let progress): - return ("sendMessageUploadVideoAction", [("progress", progress as Any)]) - case .speakingInGroupCallAction: - return ("speakingInGroupCallAction", []) + 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_sendMessageCancelAction(_ reader: BufferReader) -> SendMessageAction? { - return Api.SendMessageAction.sendMessageCancelAction - } - public static func parse_sendMessageChooseContactAction(_ reader: BufferReader) -> SendMessageAction? { - return Api.SendMessageAction.sendMessageChooseContactAction - } - public static func parse_sendMessageChooseStickerAction(_ reader: BufferReader) -> SendMessageAction? { - return Api.SendMessageAction.sendMessageChooseStickerAction - } - public static func parse_sendMessageEmojiInteraction(_ reader: BufferReader) -> SendMessageAction? { - var _1: String? - _1 = parseString(reader) - var _2: Int32? - _2 = reader.readInt32() - var _3: Api.DataJSON? + public static func parse_secureValueError(_ reader: BufferReader) -> SecureValueError? { + var _1: Api.SecureValueType? if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.DataJSON + _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.SendMessageAction.sendMessageEmojiInteraction(emoticon: _1!, msgId: _2!, interaction: _3!) + return Api.SecureValueError.secureValueError(type: _1!, hash: _2!, text: _3!) } else { return nil } } - public static func parse_sendMessageEmojiInteractionSeen(_ reader: BufferReader) -> SendMessageAction? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.SendMessageAction.sendMessageEmojiInteractionSeen(emoticon: _1!) + 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 } - else { + 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_sendMessageGamePlayAction(_ reader: BufferReader) -> SendMessageAction? { - return Api.SendMessageAction.sendMessageGamePlayAction - } - public static func parse_sendMessageGeoLocationAction(_ reader: BufferReader) -> SendMessageAction? { - return Api.SendMessageAction.sendMessageGeoLocationAction - } - public static func parse_sendMessageHistoryImportAction(_ reader: BufferReader) -> SendMessageAction? { - var _1: Int32? - _1 = reader.readInt32() + 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 - if _c1 { - return Api.SendMessageAction.sendMessageHistoryImportAction(progress: _1!) + 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_sendMessageRecordAudioAction(_ reader: BufferReader) -> SendMessageAction? { - return Api.SendMessageAction.sendMessageRecordAudioAction - } - public static func parse_sendMessageRecordRoundAction(_ reader: BufferReader) -> SendMessageAction? { - return Api.SendMessageAction.sendMessageRecordRoundAction - } - public static func parse_sendMessageRecordVideoAction(_ reader: BufferReader) -> SendMessageAction? { - return Api.SendMessageAction.sendMessageRecordVideoAction - } - public static func parse_sendMessageTypingAction(_ reader: BufferReader) -> SendMessageAction? { - return Api.SendMessageAction.sendMessageTypingAction - } - public static func parse_sendMessageUploadAudioAction(_ reader: BufferReader) -> SendMessageAction? { - var _1: Int32? - _1 = reader.readInt32() + 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 - if _c1 { - return Api.SendMessageAction.sendMessageUploadAudioAction(progress: _1!) + 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_sendMessageUploadDocumentAction(_ reader: BufferReader) -> SendMessageAction? { - var _1: Int32? - _1 = reader.readInt32() + 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 - if _c1 { - return Api.SendMessageAction.sendMessageUploadDocumentAction(progress: _1!) + 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_sendMessageUploadPhotoAction(_ reader: BufferReader) -> SendMessageAction? { - var _1: Int32? - _1 = reader.readInt32() + 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 - if _c1 { - return Api.SendMessageAction.sendMessageUploadPhotoAction(progress: _1!) + 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_sendMessageUploadRoundAction(_ reader: BufferReader) -> SendMessageAction? { - var _1: Int32? - _1 = reader.readInt32() + 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 - if _c1 { - return Api.SendMessageAction.sendMessageUploadRoundAction(progress: _1!) + 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_sendMessageUploadVideoAction(_ reader: BufferReader) -> SendMessageAction? { - var _1: Int32? - _1 = reader.readInt32() + 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 - if _c1 { - return Api.SendMessageAction.sendMessageUploadVideoAction(progress: _1!) + 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_speakingInGroupCallAction(_ reader: BufferReader) -> SendMessageAction? { - return Api.SendMessageAction.speakingInGroupCallAction - } - - } -} -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]? + 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() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.LabeledPrice.self) + _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.ShippingOption.shippingOption(id: _1!, title: _2!, prices: _3!) + return Api.SecureValueError.secureValueErrorTranslationFiles(type: _1!, fileHash: _2!, text: _3!) } else { return nil @@ -397,33 +511,39 @@ public extension Api { } } public extension Api { - enum SimpleWebViewResult: TypeConstructorDescription { - case simpleWebViewResultUrl(url: String) + enum SecureValueHash: TypeConstructorDescription { + case secureValueHash(type: Api.SecureValueType, hash: Buffer) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .simpleWebViewResultUrl(let url): + case .secureValueHash(let type, let hash): if boxed { - buffer.appendInt32(-2010155333) + buffer.appendInt32(-316748368) } - serializeString(url, buffer: buffer, boxed: false) + type.serialize(buffer, true) + serializeBytes(hash, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .simpleWebViewResultUrl(let url): - return ("simpleWebViewResultUrl", [("url", url as Any)]) + case .secureValueHash(let type, let hash): + return ("secureValueHash", [("type", type as Any), ("hash", hash as Any)]) } } - public static func parse_simpleWebViewResultUrl(_ reader: BufferReader) -> SimpleWebViewResult? { - var _1: String? - _1 = parseString(reader) + 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 - if _c1 { - return Api.SimpleWebViewResult.simpleWebViewResultUrl(url: _1!) + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.SecureValueHash.secureValueHash(type: _1!, hash: _2!) } else { return nil @@ -433,139 +553,211 @@ public extension Api { } } public extension Api { - indirect enum SponsoredMessage: TypeConstructorDescription { - case sponsoredMessage(flags: Int32, randomId: Buffer, fromId: Api.Peer?, chatInvite: Api.ChatInvite?, chatInviteHash: String?, channelPost: Int32?, startParam: String?, webpage: Api.SponsoredWebPage?, message: String, entities: [Api.MessageEntity]?, sponsorInfo: String?, additionalInfo: String?) + 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 .sponsoredMessage(let flags, let randomId, let fromId, let chatInvite, let chatInviteHash, let channelPost, let startParam, let webpage, let message, let entities, let sponsorInfo, let additionalInfo): + case .secureValueTypeAddress: if boxed { - buffer.appendInt32(-626000021) + buffer.appendInt32(-874308058) } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeBytes(randomId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 3) != 0 {fromId!.serialize(buffer, true)} - if Int(flags) & Int(1 << 4) != 0 {chatInvite!.serialize(buffer, true)} - if Int(flags) & Int(1 << 4) != 0 {serializeString(chatInviteHash!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeInt32(channelPost!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 0) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 9) != 0 {webpage!.serialize(buffer, true)} - 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 << 7) != 0 {serializeString(sponsorInfo!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 8) != 0 {serializeString(additionalInfo!, buffer: buffer, boxed: false)} + + 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 .sponsoredMessage(let flags, let randomId, let fromId, let chatInvite, let chatInviteHash, let channelPost, let startParam, let webpage, let message, let entities, let sponsorInfo, let additionalInfo): - return ("sponsoredMessage", [("flags", flags as Any), ("randomId", randomId as Any), ("fromId", fromId as Any), ("chatInvite", chatInvite as Any), ("chatInviteHash", chatInviteHash as Any), ("channelPost", channelPost as Any), ("startParam", startParam as Any), ("webpage", webpage as Any), ("message", message as Any), ("entities", entities as Any), ("sponsorInfo", sponsorInfo as Any), ("additionalInfo", additionalInfo as Any)]) + 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_sponsoredMessage(_ reader: BufferReader) -> SponsoredMessage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Buffer? - _2 = parseBytes(reader) - var _3: Api.Peer? - if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.Peer - } } - var _4: Api.ChatInvite? - if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.ChatInvite - } } - var _5: String? - if Int(_1!) & Int(1 << 4) != 0 {_5 = parseString(reader) } - var _6: Int32? - if Int(_1!) & Int(1 << 2) != 0 {_6 = reader.readInt32() } - var _7: String? - if Int(_1!) & Int(1 << 0) != 0 {_7 = parseString(reader) } - var _8: Api.SponsoredWebPage? - if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.SponsoredWebPage - } } - var _9: String? - _9 = parseString(reader) - var _10: [Api.MessageEntity]? - if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { - _10 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) - } } - 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) } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 3) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 4) == 0) || _4 != nil - let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil - let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil - let _c8 = (Int(_1!) & Int(1 << 9) == 0) || _8 != nil - let _c9 = _9 != nil - let _c10 = (Int(_1!) & Int(1 << 1) == 0) || _10 != nil - let _c11 = (Int(_1!) & Int(1 << 7) == 0) || _11 != nil - let _c12 = (Int(_1!) & Int(1 << 8) == 0) || _12 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 { - return Api.SponsoredMessage.sponsoredMessage(flags: _1!, randomId: _2!, fromId: _3, chatInvite: _4, chatInviteHash: _5, channelPost: _6, startParam: _7, webpage: _8, message: _9!, entities: _10, sponsorInfo: _11, additionalInfo: _12) - } - else { - return nil - } + 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 SponsoredWebPage: TypeConstructorDescription { - case sponsoredWebPage(flags: Int32, url: String, siteName: String, photo: Api.Photo?) + enum SendAsPeer: TypeConstructorDescription { + case sendAsPeer(flags: Int32, peer: Api.Peer) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .sponsoredWebPage(let flags, let url, let siteName, let photo): + case .sendAsPeer(let flags, let peer): if boxed { - buffer.appendInt32(1035529315) + buffer.appendInt32(-1206095820) } serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(url, buffer: buffer, boxed: false) - serializeString(siteName, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {photo!.serialize(buffer, true)} + peer.serialize(buffer, true) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .sponsoredWebPage(let flags, let url, let siteName, let photo): - return ("sponsoredWebPage", [("flags", flags as Any), ("url", url as Any), ("siteName", siteName as Any), ("photo", photo as Any)]) + case .sendAsPeer(let flags, let peer): + return ("sendAsPeer", [("flags", flags as Any), ("peer", peer as Any)]) } } - public static func parse_sponsoredWebPage(_ reader: BufferReader) -> SponsoredWebPage? { + public static func parse_sendAsPeer(_ reader: BufferReader) -> SendAsPeer? { var _1: Int32? _1 = reader.readInt32() - var _2: String? - _2 = parseString(reader) - var _3: String? - _3 = parseString(reader) - var _4: Api.Photo? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.Photo - } } + var _2: Api.Peer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Peer + } 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.SponsoredWebPage.sponsoredWebPage(flags: _1!, url: _2!, siteName: _3!, photo: _4) + if _c1 && _c2 { + return Api.SendAsPeer.sendAsPeer(flags: _1!, peer: _2!) } else { return nil @@ -575,340 +767,308 @@ 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) + enum SendMessageAction: TypeConstructorDescription { + case sendMessageCancelAction + case sendMessageChooseContactAction + case sendMessageChooseStickerAction + case sendMessageEmojiInteraction(emoticon: String, msgId: Int32, interaction: Api.DataJSON) + case sendMessageEmojiInteractionSeen(emoticon: String) + case sendMessageGamePlayAction + case sendMessageGeoLocationAction + case sendMessageHistoryImportAction(progress: Int32) + case sendMessageRecordAudioAction + case sendMessageRecordRoundAction + case sendMessageRecordVideoAction + case sendMessageTypingAction + case sendMessageUploadAudioAction(progress: Int32) + case sendMessageUploadDocumentAction(progress: Int32) + case sendMessageUploadPhotoAction(progress: Int32) + case sendMessageUploadRoundAction(progress: Int32) + case sendMessageUploadVideoAction(progress: Int32) + case speakingInGroupCallAction public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .statsDateRangeDays(let minDate, let maxDate): + case .sendMessageCancelAction: if boxed { - buffer.appendInt32(-1237848657) + buffer.appendInt32(-44119819) } - 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): + case .sendMessageChooseContactAction: if boxed { - buffer.appendInt32(-1901828938) + buffer.appendInt32(1653390447) } - 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): + case .sendMessageChooseStickerAction: if boxed { - buffer.appendInt32(1244130093) + buffer.appendInt32(-1336228175) } - serializeString(token, buffer: buffer, boxed: false) + break - case .statsGraphError(let error): + case .sendMessageEmojiInteraction(let emoticon, let msgId, let interaction): if boxed { - buffer.appendInt32(-1092839390) + buffer.appendInt32(630664139) } - serializeString(error, buffer: buffer, boxed: false) + serializeString(emoticon, buffer: buffer, boxed: false) + serializeInt32(msgId, buffer: buffer, boxed: false) + interaction.serialize(buffer, true) + break + case .sendMessageEmojiInteractionSeen(let emoticon): + if boxed { + buffer.appendInt32(-1234857938) + } + serializeString(emoticon, buffer: buffer, boxed: false) + break + case .sendMessageGamePlayAction: + if boxed { + buffer.appendInt32(-580219064) + } + + break + case .sendMessageGeoLocationAction: + if boxed { + buffer.appendInt32(393186209) + } + + break + case .sendMessageHistoryImportAction(let progress): + if boxed { + buffer.appendInt32(-606432698) + } + serializeInt32(progress, buffer: buffer, boxed: false) + break + case .sendMessageRecordAudioAction: + if boxed { + buffer.appendInt32(-718310409) + } + + break + case .sendMessageRecordRoundAction: + if boxed { + buffer.appendInt32(-1997373508) + } + + break + case .sendMessageRecordVideoAction: + if boxed { + buffer.appendInt32(-1584933265) + } + + break + case .sendMessageTypingAction: + if boxed { + buffer.appendInt32(381645902) + } + + break + case .sendMessageUploadAudioAction(let progress): + if boxed { + buffer.appendInt32(-212740181) + } + serializeInt32(progress, buffer: buffer, boxed: false) + break + case .sendMessageUploadDocumentAction(let progress): + if boxed { + buffer.appendInt32(-1441998364) + } + serializeInt32(progress, buffer: buffer, boxed: false) + break + case .sendMessageUploadPhotoAction(let progress): + if boxed { + buffer.appendInt32(-774682074) + } + serializeInt32(progress, buffer: buffer, boxed: false) + break + case .sendMessageUploadRoundAction(let progress): + if boxed { + buffer.appendInt32(608050278) + } + serializeInt32(progress, buffer: buffer, boxed: false) + break + case .sendMessageUploadVideoAction(let progress): + if boxed { + buffer.appendInt32(-378127636) + } + serializeInt32(progress, buffer: buffer, boxed: false) + break + case .speakingInGroupCallAction: + if boxed { + buffer.appendInt32(-651419003) + } + 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)]) + case .sendMessageCancelAction: + return ("sendMessageCancelAction", []) + case .sendMessageChooseContactAction: + return ("sendMessageChooseContactAction", []) + case .sendMessageChooseStickerAction: + return ("sendMessageChooseStickerAction", []) + case .sendMessageEmojiInteraction(let emoticon, let msgId, let interaction): + return ("sendMessageEmojiInteraction", [("emoticon", emoticon as Any), ("msgId", msgId as Any), ("interaction", interaction as Any)]) + case .sendMessageEmojiInteractionSeen(let emoticon): + return ("sendMessageEmojiInteractionSeen", [("emoticon", emoticon as Any)]) + case .sendMessageGamePlayAction: + return ("sendMessageGamePlayAction", []) + case .sendMessageGeoLocationAction: + return ("sendMessageGeoLocationAction", []) + case .sendMessageHistoryImportAction(let progress): + return ("sendMessageHistoryImportAction", [("progress", progress as Any)]) + case .sendMessageRecordAudioAction: + return ("sendMessageRecordAudioAction", []) + case .sendMessageRecordRoundAction: + return ("sendMessageRecordRoundAction", []) + case .sendMessageRecordVideoAction: + return ("sendMessageRecordVideoAction", []) + case .sendMessageTypingAction: + return ("sendMessageTypingAction", []) + case .sendMessageUploadAudioAction(let progress): + return ("sendMessageUploadAudioAction", [("progress", progress as Any)]) + case .sendMessageUploadDocumentAction(let progress): + return ("sendMessageUploadDocumentAction", [("progress", progress as Any)]) + case .sendMessageUploadPhotoAction(let progress): + return ("sendMessageUploadPhotoAction", [("progress", progress as Any)]) + case .sendMessageUploadRoundAction(let progress): + return ("sendMessageUploadRoundAction", [("progress", progress as Any)]) + case .sendMessageUploadVideoAction(let progress): + return ("sendMessageUploadVideoAction", [("progress", progress as Any)]) + case .speakingInGroupCallAction: + return ("speakingInGroupCallAction", []) } } - public static func parse_statsGraph(_ reader: BufferReader) -> StatsGraph? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.DataJSON? + public static func parse_sendMessageCancelAction(_ reader: BufferReader) -> SendMessageAction? { + return Api.SendMessageAction.sendMessageCancelAction + } + public static func parse_sendMessageChooseContactAction(_ reader: BufferReader) -> SendMessageAction? { + return Api.SendMessageAction.sendMessageChooseContactAction + } + public static func parse_sendMessageChooseStickerAction(_ reader: BufferReader) -> SendMessageAction? { + return Api.SendMessageAction.sendMessageChooseStickerAction + } + public static func parse_sendMessageEmojiInteraction(_ reader: BufferReader) -> SendMessageAction? { + var _1: String? + _1 = parseString(reader) + var _2: Int32? + _2 = reader.readInt32() + var _3: Api.DataJSON? if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.DataJSON + _3 = 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 + let _c3 = _3 != nil if _c1 && _c2 && _c3 { - return Api.StatsGraph.statsGraph(flags: _1!, json: _2!, zoomToken: _3) + return Api.SendMessageAction.sendMessageEmojiInteraction(emoticon: _1!, msgId: _2!, interaction: _3!) } else { return nil } } - public static func parse_statsGraphAsync(_ reader: BufferReader) -> StatsGraph? { + public static func parse_sendMessageEmojiInteractionSeen(_ reader: BufferReader) -> SendMessageAction? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil if _c1 { - return Api.StatsGraph.statsGraphAsync(token: _1!) + return Api.SendMessageAction.sendMessageEmojiInteractionSeen(emoticon: _1!) } else { return nil } } - public static func parse_statsGraphError(_ reader: BufferReader) -> StatsGraph? { - var _1: String? - _1 = parseString(reader) + public static func parse_sendMessageGamePlayAction(_ reader: BufferReader) -> SendMessageAction? { + return Api.SendMessageAction.sendMessageGamePlayAction + } + public static func parse_sendMessageGeoLocationAction(_ reader: BufferReader) -> SendMessageAction? { + return Api.SendMessageAction.sendMessageGeoLocationAction + } + public static func parse_sendMessageHistoryImportAction(_ reader: BufferReader) -> SendMessageAction? { + var _1: Int32? + _1 = reader.readInt32() let _c1 = _1 != nil if _c1 { - return Api.StatsGraph.statsGraphError(error: _1!) + return Api.SendMessageAction.sendMessageHistoryImportAction(progress: _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() + public static func parse_sendMessageRecordAudioAction(_ reader: BufferReader) -> SendMessageAction? { + return Api.SendMessageAction.sendMessageRecordAudioAction + } + public static func parse_sendMessageRecordRoundAction(_ reader: BufferReader) -> SendMessageAction? { + return Api.SendMessageAction.sendMessageRecordRoundAction + } + public static func parse_sendMessageRecordVideoAction(_ reader: BufferReader) -> SendMessageAction? { + return Api.SendMessageAction.sendMessageRecordVideoAction + } + public static func parse_sendMessageTypingAction(_ reader: BufferReader) -> SendMessageAction? { + return Api.SendMessageAction.sendMessageTypingAction + } + public static func parse_sendMessageUploadAudioAction(_ reader: BufferReader) -> SendMessageAction? { + var _1: Int32? + _1 = 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!) + if _c1 { + return Api.SendMessageAction.sendMessageUploadAudioAction(progress: _1!) } 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() + public static func parse_sendMessageUploadDocumentAction(_ reader: BufferReader) -> SendMessageAction? { + var _1: Int32? + _1 = reader.readInt32() let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StatsGroupTopInviter.statsGroupTopInviter(userId: _1!, invitations: _2!) + if _c1 { + return Api.SendMessageAction.sendMessageUploadDocumentAction(progress: _1!) } 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() + public static func parse_sendMessageUploadPhotoAction(_ reader: BufferReader) -> SendMessageAction? { + var _1: Int32? + _1 = 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!) + if _c1 { + return Api.SendMessageAction.sendMessageUploadPhotoAction(progress: _1!) } 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() + public static func parse_sendMessageUploadRoundAction(_ reader: BufferReader) -> SendMessageAction? { + var _1: Int32? + _1 = reader.readInt32() let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StatsPercentValue.statsPercentValue(part: _1!, total: _2!) + if _c1 { + return Api.SendMessageAction.sendMessageUploadRoundAction(progress: _1!) } else { return nil } } + public static func parse_sendMessageUploadVideoAction(_ reader: BufferReader) -> SendMessageAction? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.SendMessageAction.sendMessageUploadVideoAction(progress: _1!) + } + else { + return nil + } + } + public static func parse_speakingInGroupCallAction(_ reader: BufferReader) -> SendMessageAction? { + return Api.SendMessageAction.speakingInGroupCallAction + } } } diff --git a/submodules/TelegramApi/Sources/Api21.swift b/submodules/TelegramApi/Sources/Api21.swift index 5baa2dc1c4c..82fa5e77566 100644 --- a/submodules/TelegramApi/Sources/Api21.swift +++ b/submodules/TelegramApi/Sources/Api21.swift @@ -1,31 +1,45 @@ public extension Api { - enum StatsURL: TypeConstructorDescription { - case statsURL(url: String) + enum ShippingOption: TypeConstructorDescription { + case shippingOption(id: String, title: String, prices: [Api.LabeledPrice]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .statsURL(let url): + case .shippingOption(let id, let title, let prices): if boxed { - buffer.appendInt32(1202287072) + 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) } - 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)]) + 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_statsURL(_ reader: BufferReader) -> StatsURL? { + 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 - if _c1 { - return Api.StatsURL.statsURL(url: _1!) + 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 @@ -35,43 +49,33 @@ public extension Api { } } public extension Api { - enum StickerKeyword: TypeConstructorDescription { - case stickerKeyword(documentId: Int64, keyword: [String]) + enum SimpleWebViewResult: TypeConstructorDescription { + case simpleWebViewResultUrl(url: String) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .stickerKeyword(let documentId, let keyword): + case .simpleWebViewResultUrl(let url): 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) + buffer.appendInt32(-2010155333) } + serializeString(url, 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)]) + case .simpleWebViewResultUrl(let url): + return ("simpleWebViewResultUrl", [("url", url 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) - } + public static func parse_simpleWebViewResultUrl(_ reader: BufferReader) -> SimpleWebViewResult? { + var _1: String? + _1 = parseString(reader) let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StickerKeyword.stickerKeyword(documentId: _1!, keyword: _2!) + if _c1 { + return Api.SimpleWebViewResult.simpleWebViewResultUrl(url: _1!) } else { return nil @@ -81,43 +85,99 @@ public extension Api { } } public extension Api { - enum StickerPack: TypeConstructorDescription { - case stickerPack(emoticon: String, documents: [Int64]) + indirect enum SponsoredMessage: TypeConstructorDescription { + case sponsoredMessage(flags: Int32, randomId: Buffer, fromId: Api.Peer?, chatInvite: Api.ChatInvite?, chatInviteHash: String?, channelPost: Int32?, startParam: String?, webpage: Api.SponsoredWebPage?, app: Api.BotApp?, message: String, entities: [Api.MessageEntity]?, buttonText: String?, sponsorInfo: String?, additionalInfo: String?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .stickerPack(let emoticon, let documents): + case .sponsoredMessage(let flags, let randomId, let fromId, let chatInvite, let chatInviteHash, let channelPost, let startParam, let webpage, let app, let message, let entities, let buttonText, let sponsorInfo, let additionalInfo): if boxed { - buffer.appendInt32(313694676) - } - serializeString(emoticon, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(documents.count)) - for item in documents { - serializeInt64(item, buffer: buffer, boxed: false) + buffer.appendInt32(-313293833) } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeBytes(randomId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 3) != 0 {fromId!.serialize(buffer, true)} + if Int(flags) & Int(1 << 4) != 0 {chatInvite!.serialize(buffer, true)} + if Int(flags) & Int(1 << 4) != 0 {serializeString(chatInviteHash!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeInt32(channelPost!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 9) != 0 {webpage!.serialize(buffer, true)} + if Int(flags) & Int(1 << 10) != 0 {app!.serialize(buffer, true)} + 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 << 11) != 0 {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)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .stickerPack(let emoticon, let documents): - return ("stickerPack", [("emoticon", emoticon as Any), ("documents", documents as Any)]) + case .sponsoredMessage(let flags, let randomId, let fromId, let chatInvite, let chatInviteHash, let channelPost, let startParam, let webpage, let app, let message, let entities, let buttonText, let sponsorInfo, let additionalInfo): + return ("sponsoredMessage", [("flags", flags as Any), ("randomId", randomId as Any), ("fromId", fromId as Any), ("chatInvite", chatInvite as Any), ("chatInviteHash", chatInviteHash as Any), ("channelPost", channelPost as Any), ("startParam", startParam as Any), ("webpage", webpage as Any), ("app", app as Any), ("message", message as Any), ("entities", entities as Any), ("buttonText", buttonText as Any), ("sponsorInfo", sponsorInfo as Any), ("additionalInfo", additionalInfo as Any)]) } } - public static func parse_stickerPack(_ reader: BufferReader) -> StickerPack? { - var _1: String? - _1 = parseString(reader) - var _2: [Int64]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) - } + public static func parse_sponsoredMessage(_ reader: BufferReader) -> SponsoredMessage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Buffer? + _2 = parseBytes(reader) + var _3: Api.Peer? + if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.Peer + } } + var _4: Api.ChatInvite? + if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.ChatInvite + } } + var _5: String? + if Int(_1!) & Int(1 << 4) != 0 {_5 = parseString(reader) } + var _6: Int32? + if Int(_1!) & Int(1 << 2) != 0 {_6 = reader.readInt32() } + var _7: String? + if Int(_1!) & Int(1 << 0) != 0 {_7 = parseString(reader) } + var _8: Api.SponsoredWebPage? + if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.SponsoredWebPage + } } + var _9: Api.BotApp? + if Int(_1!) & Int(1 << 10) != 0 {if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.BotApp + } } + var _10: String? + _10 = parseString(reader) + var _11: [Api.MessageEntity]? + if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { + _11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) + } } + var _12: String? + if Int(_1!) & Int(1 << 11) != 0 {_12 = parseString(reader) } + var _13: String? + if Int(_1!) & Int(1 << 7) != 0 {_13 = parseString(reader) } + var _14: String? + if Int(_1!) & Int(1 << 8) != 0 {_14 = parseString(reader) } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StickerPack.stickerPack(emoticon: _1!, documents: _2!) + let _c3 = (Int(_1!) & Int(1 << 3) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 4) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil + let _c8 = (Int(_1!) & Int(1 << 9) == 0) || _8 != nil + let _c9 = (Int(_1!) & Int(1 << 10) == 0) || _9 != nil + let _c10 = _10 != nil + let _c11 = (Int(_1!) & Int(1 << 1) == 0) || _11 != nil + let _c12 = (Int(_1!) & Int(1 << 11) == 0) || _12 != nil + let _c13 = (Int(_1!) & Int(1 << 7) == 0) || _13 != nil + let _c14 = (Int(_1!) & Int(1 << 8) == 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!, fromId: _3, chatInvite: _4, chatInviteHash: _5, channelPost: _6, startParam: _7, webpage: _8, app: _9, message: _10!, entities: _11, buttonText: _12, sponsorInfo: _13, additionalInfo: _14) } else { return nil @@ -127,83 +187,47 @@ public extension Api { } } public extension Api { - enum StickerSet: TypeConstructorDescription { - case stickerSet(flags: Int32, installedDate: Int32?, id: Int64, accessHash: Int64, title: String, shortName: String, thumbs: [Api.PhotoSize]?, thumbDcId: Int32?, thumbVersion: Int32?, thumbDocumentId: Int64?, count: Int32, hash: Int32) + enum SponsoredWebPage: TypeConstructorDescription { + case sponsoredWebPage(flags: Int32, url: String, siteName: String, photo: Api.Photo?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .stickerSet(let flags, let installedDate, let id, let accessHash, let title, let shortName, let thumbs, let thumbDcId, let thumbVersion, let thumbDocumentId, let count, let hash): + case .sponsoredWebPage(let flags, let url, let siteName, let photo): if boxed { - buffer.appendInt32(768691932) + buffer.appendInt32(1035529315) } serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(installedDate!, buffer: buffer, boxed: false)} - serializeInt64(id, buffer: buffer, boxed: false) - serializeInt64(accessHash, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - serializeString(shortName, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(thumbs!.count)) - for item in thumbs! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 4) != 0 {serializeInt32(thumbDcId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {serializeInt32(thumbVersion!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 8) != 0 {serializeInt64(thumbDocumentId!, buffer: buffer, boxed: false)} - serializeInt32(count, buffer: buffer, boxed: false) - serializeInt32(hash, buffer: buffer, boxed: false) + serializeString(url, buffer: buffer, boxed: false) + serializeString(siteName, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {photo!.serialize(buffer, true)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .stickerSet(let flags, let installedDate, let id, let accessHash, let title, let shortName, let thumbs, let thumbDcId, let thumbVersion, let thumbDocumentId, let count, let hash): - return ("stickerSet", [("flags", flags as Any), ("installedDate", installedDate as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("title", title as Any), ("shortName", shortName as Any), ("thumbs", thumbs as Any), ("thumbDcId", thumbDcId as Any), ("thumbVersion", thumbVersion as Any), ("thumbDocumentId", thumbDocumentId as Any), ("count", count as Any), ("hash", hash as Any)]) + case .sponsoredWebPage(let flags, let url, let siteName, let photo): + return ("sponsoredWebPage", [("flags", flags as Any), ("url", url as Any), ("siteName", siteName as Any), ("photo", photo as Any)]) } } - public static func parse_stickerSet(_ reader: BufferReader) -> StickerSet? { + public static func parse_sponsoredWebPage(_ reader: BufferReader) -> SponsoredWebPage? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } - var _3: Int64? - _3 = reader.readInt64() - var _4: Int64? - _4 = reader.readInt64() - var _5: String? - _5 = parseString(reader) - var _6: String? - _6 = parseString(reader) - var _7: [Api.PhotoSize]? - if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() { - _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PhotoSize.self) + var _2: String? + _2 = parseString(reader) + var _3: String? + _3 = parseString(reader) + var _4: Api.Photo? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.Photo } } - var _8: Int32? - if Int(_1!) & Int(1 << 4) != 0 {_8 = reader.readInt32() } - var _9: Int32? - if Int(_1!) & Int(1 << 4) != 0 {_9 = reader.readInt32() } - var _10: Int64? - if Int(_1!) & Int(1 << 8) != 0 {_10 = reader.readInt64() } - var _11: Int32? - _11 = reader.readInt32() - var _12: Int32? - _12 = reader.readInt32() let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != 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 << 4) == 0) || _7 != nil - let _c8 = (Int(_1!) & Int(1 << 4) == 0) || _8 != nil - let _c9 = (Int(_1!) & Int(1 << 4) == 0) || _9 != nil - let _c10 = (Int(_1!) & Int(1 << 8) == 0) || _10 != nil - let _c11 = _11 != nil - let _c12 = _12 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 { - return Api.StickerSet.stickerSet(flags: _1!, installedDate: _2, id: _3!, accessHash: _4!, title: _5!, shortName: _6!, thumbs: _7, thumbDcId: _8, thumbVersion: _9, thumbDocumentId: _10, count: _11!, hash: _12!) + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.SponsoredWebPage.sponsoredWebPage(flags: _1!, url: _2!, siteName: _3!, photo: _4) } else { return nil @@ -213,147 +237,163 @@ public extension Api { } } public extension Api { - enum StickerSetCovered: TypeConstructorDescription { - case stickerSetCovered(set: Api.StickerSet, cover: Api.Document) - case stickerSetFullCovered(set: Api.StickerSet, packs: [Api.StickerPack], keywords: [Api.StickerKeyword], documents: [Api.Document]) - case stickerSetMultiCovered(set: Api.StickerSet, covers: [Api.Document]) - case stickerSetNoCovered(set: Api.StickerSet) + enum StatsAbsValueAndPrev: TypeConstructorDescription { + case statsAbsValueAndPrev(current: Double, previous: Double) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .stickerSetCovered(let set, let cover): - if boxed { - buffer.appendInt32(1678812626) - } - set.serialize(buffer, true) - cover.serialize(buffer, true) - break - case .stickerSetFullCovered(let set, let packs, let keywords, let documents): - if boxed { - buffer.appendInt32(1087454222) - } - set.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(packs.count)) - for item in packs { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(keywords.count)) - for item in keywords { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(documents.count)) - for item in documents { - item.serialize(buffer, true) - } - break - case .stickerSetMultiCovered(let set, let covers): + case .statsAbsValueAndPrev(let current, let previous): if boxed { - buffer.appendInt32(872932635) - } - set.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(covers.count)) - for item in covers { - item.serialize(buffer, true) + buffer.appendInt32(-884757282) } + serializeDouble(current, buffer: buffer, boxed: false) + serializeDouble(previous, buffer: buffer, boxed: false) break - case .stickerSetNoCovered(let set): + } + } + + 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(2008112412) + buffer.appendInt32(-1237848657) } - set.serialize(buffer, true) + serializeInt32(minDate, buffer: buffer, boxed: false) + serializeInt32(maxDate, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .stickerSetCovered(let set, let cover): - return ("stickerSetCovered", [("set", set as Any), ("cover", cover as Any)]) - case .stickerSetFullCovered(let set, let packs, let keywords, let documents): - return ("stickerSetFullCovered", [("set", set as Any), ("packs", packs as Any), ("keywords", keywords as Any), ("documents", documents as Any)]) - case .stickerSetMultiCovered(let set, let covers): - return ("stickerSetMultiCovered", [("set", set as Any), ("covers", covers as Any)]) - case .stickerSetNoCovered(let set): - return ("stickerSetNoCovered", [("set", set as Any)]) + case .statsDateRangeDays(let minDate, let maxDate): + return ("statsDateRangeDays", [("minDate", minDate as Any), ("maxDate", maxDate as Any)]) } } - public static func parse_stickerSetCovered(_ reader: BufferReader) -> StickerSetCovered? { - var _1: Api.StickerSet? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.StickerSet - } - var _2: Api.Document? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.Document - } + 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.StickerSetCovered.stickerSetCovered(set: _1!, cover: _2!) + return Api.StatsDateRangeDays.statsDateRangeDays(minDate: _1!, maxDate: _2!) } else { return nil } } - public static func parse_stickerSetFullCovered(_ reader: BufferReader) -> StickerSetCovered? { - var _1: Api.StickerSet? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.StickerSet - } - var _2: [Api.StickerPack]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self) - } - var _3: [Api.StickerKeyword]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerKeyword.self) - } - var _4: [Api.Document]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + + } +} +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 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.StickerSetCovered.stickerSetFullCovered(set: _1!, packs: _2!, keywords: _3!, documents: _4!) + 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_stickerSetMultiCovered(_ reader: BufferReader) -> StickerSetCovered? { - var _1: Api.StickerSet? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.StickerSet - } - var _2: [Api.Document]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) - } + public static func parse_statsGraphAsync(_ reader: BufferReader) -> StatsGraph? { + var _1: String? + _1 = parseString(reader) let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StickerSetCovered.stickerSetMultiCovered(set: _1!, covers: _2!) + if _c1 { + return Api.StatsGraph.statsGraphAsync(token: _1!) } else { return nil } } - public static func parse_stickerSetNoCovered(_ reader: BufferReader) -> StickerSetCovered? { - var _1: Api.StickerSet? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.StickerSet - } + public static func parse_statsGraphError(_ reader: BufferReader) -> StatsGraph? { + var _1: String? + _1 = parseString(reader) let _c1 = _1 != nil if _c1 { - return Api.StickerSetCovered.stickerSetNoCovered(set: _1!) + return Api.StatsGraph.statsGraphError(error: _1!) } else { return nil @@ -363,41 +403,45 @@ public extension Api { } } public extension Api { - enum StoriesStealthMode: TypeConstructorDescription { - case storiesStealthMode(flags: Int32, activeUntilDate: Int32?, cooldownUntilDate: Int32?) + enum StatsGroupTopAdmin: TypeConstructorDescription { + case statsGroupTopAdmin(userId: Int64, deleted: Int32, kicked: Int32, banned: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .storiesStealthMode(let flags, let activeUntilDate, let cooldownUntilDate): + case .statsGroupTopAdmin(let userId, let deleted, let kicked, let banned): if boxed { - buffer.appendInt32(1898850301) + buffer.appendInt32(-682079097) } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(activeUntilDate!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(cooldownUntilDate!, buffer: buffer, boxed: false)} + 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 .storiesStealthMode(let flags, let activeUntilDate, let cooldownUntilDate): - return ("storiesStealthMode", [("flags", flags as Any), ("activeUntilDate", activeUntilDate as Any), ("cooldownUntilDate", cooldownUntilDate as Any)]) + 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_storiesStealthMode(_ reader: BufferReader) -> StoriesStealthMode? { - var _1: Int32? - _1 = reader.readInt32() + public static func parse_statsGroupTopAdmin(_ reader: BufferReader) -> StatsGroupTopAdmin? { + var _1: Int64? + _1 = reader.readInt64() var _2: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } + _2 = reader.readInt32() var _3: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() } + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.StoriesStealthMode.storiesStealthMode(flags: _1!, activeUntilDate: _2, cooldownUntilDate: _3) + 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 @@ -407,149 +451,37 @@ public extension Api { } } public extension Api { - indirect enum StoryItem: TypeConstructorDescription { - case storyItem(flags: Int32, id: Int32, date: Int32, expireDate: Int32, caption: String?, entities: [Api.MessageEntity]?, media: Api.MessageMedia, mediaAreas: [Api.MediaArea]?, privacy: [Api.PrivacyRule]?, views: Api.StoryViews?, sentReaction: Api.Reaction?) - case storyItemDeleted(id: Int32) - case storyItemSkipped(flags: Int32, id: Int32, date: Int32, expireDate: Int32) + enum StatsGroupTopInviter: TypeConstructorDescription { + case statsGroupTopInviter(userId: Int64, invitations: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .storyItem(let flags, let id, let date, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction): - if boxed { - buffer.appendInt32(1153718222) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(id, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt32(expireDate, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(caption!, 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) - }} - media.serialize(buffer, true) - if Int(flags) & Int(1 << 14) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(mediaAreas!.count)) - for item in mediaAreas! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 2) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(privacy!.count)) - for item in privacy! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 3) != 0 {views!.serialize(buffer, true)} - if Int(flags) & Int(1 << 15) != 0 {sentReaction!.serialize(buffer, true)} - break - case .storyItemDeleted(let id): - if boxed { - buffer.appendInt32(1374088783) - } - serializeInt32(id, buffer: buffer, boxed: false) - break - case .storyItemSkipped(let flags, let id, let date, let expireDate): + case .statsGroupTopInviter(let userId, let invitations): if boxed { - buffer.appendInt32(-5388013) + buffer.appendInt32(1398765469) } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(id, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt32(expireDate, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + serializeInt32(invitations, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .storyItem(let flags, let id, let date, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction): - return ("storyItem", [("flags", flags as Any), ("id", id as Any), ("date", date as Any), ("expireDate", expireDate as Any), ("caption", caption as Any), ("entities", entities as Any), ("media", media as Any), ("mediaAreas", mediaAreas as Any), ("privacy", privacy as Any), ("views", views as Any), ("sentReaction", sentReaction as Any)]) - case .storyItemDeleted(let id): - return ("storyItemDeleted", [("id", id as Any)]) - case .storyItemSkipped(let flags, let id, let date, let expireDate): - return ("storyItemSkipped", [("flags", flags as Any), ("id", id as Any), ("date", date as Any), ("expireDate", expireDate as Any)]) + case .statsGroupTopInviter(let userId, let invitations): + return ("statsGroupTopInviter", [("userId", userId as Any), ("invitations", invitations as Any)]) } } - public static func parse_storyItem(_ reader: BufferReader) -> StoryItem? { - 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: String? - if Int(_1!) & Int(1 << 0) != 0 {_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.MessageMedia? - if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.MessageMedia - } - var _8: [Api.MediaArea]? - if Int(_1!) & Int(1 << 14) != 0 {if let _ = reader.readInt32() { - _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MediaArea.self) - } } - var _9: [Api.PrivacyRule]? - if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() { - _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PrivacyRule.self) - } } - var _10: Api.StoryViews? - if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { - _10 = Api.parse(reader, signature: signature) as? Api.StoryViews - } } - var _11: Api.Reaction? - if Int(_1!) & Int(1 << 15) != 0 {if let signature = reader.readInt32() { - _11 = Api.parse(reader, signature: signature) as? Api.Reaction - } } - 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 << 1) == 0) || _6 != nil - let _c7 = _7 != nil - let _c8 = (Int(_1!) & Int(1 << 14) == 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 << 15) == 0) || _11 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { - return Api.StoryItem.storyItem(flags: _1!, id: _2!, date: _3!, expireDate: _4!, caption: _5, entities: _6, media: _7!, mediaAreas: _8, privacy: _9, views: _10, sentReaction: _11) - } - else { - return nil - } - } - public static func parse_storyItemDeleted(_ reader: BufferReader) -> StoryItem? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.StoryItem.storyItemDeleted(id: _1!) - } - else { - return nil - } - } - public static func parse_storyItemSkipped(_ reader: BufferReader) -> StoryItem? { - var _1: Int32? - _1 = reader.readInt32() + public static func parse_statsGroupTopInviter(_ reader: BufferReader) -> StatsGroupTopInviter? { + 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.StoryItem.storyItemSkipped(flags: _1!, id: _2!, date: _3!, expireDate: _4!) + if _c1 && _c2 { + return Api.StatsGroupTopInviter.statsGroupTopInviter(userId: _1!, invitations: _2!) } else { return nil @@ -559,47 +491,41 @@ public extension Api { } } public extension Api { - enum StoryView: TypeConstructorDescription { - case storyView(flags: Int32, userId: Int64, date: Int32, reaction: Api.Reaction?) + enum StatsGroupTopPoster: TypeConstructorDescription { + case statsGroupTopPoster(userId: Int64, messages: Int32, avgChars: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .storyView(let flags, let userId, let date, let reaction): + case .statsGroupTopPoster(let userId, let messages, let avgChars): if boxed { - buffer.appendInt32(-1329730875) + buffer.appendInt32(-1660637285) } - serializeInt32(flags, buffer: buffer, boxed: false) serializeInt64(userId, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {reaction!.serialize(buffer, true)} + serializeInt32(messages, buffer: buffer, boxed: false) + serializeInt32(avgChars, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .storyView(let flags, let userId, let date, let reaction): - return ("storyView", [("flags", flags as Any), ("userId", userId as Any), ("date", date as Any), ("reaction", reaction as Any)]) + 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_storyView(_ reader: BufferReader) -> StoryView? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() + 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() - var _4: Api.Reaction? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.Reaction - } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.StoryView.storyView(flags: _1!, userId: _2!, date: _3!, reaction: _4) + if _c1 && _c2 && _c3 { + return Api.StatsGroupTopPoster.statsGroupTopPoster(userId: _1!, messages: _2!, avgChars: _3!) } else { return nil @@ -609,65 +535,37 @@ public extension Api { } } public extension Api { - enum StoryViews: TypeConstructorDescription { - case storyViews(flags: Int32, viewsCount: Int32, forwardsCount: Int32?, reactions: [Api.ReactionCount]?, reactionsCount: Int32?, recentViewers: [Int64]?) + enum StatsPercentValue: TypeConstructorDescription { + case statsPercentValue(part: Double, total: Double) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .storyViews(let flags, let viewsCount, let forwardsCount, let reactions, let reactionsCount, let recentViewers): + case .statsPercentValue(let part, let total): if boxed { - buffer.appendInt32(-1923523370) + buffer.appendInt32(-875679776) } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(viewsCount, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {serializeInt32(forwardsCount!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(reactions!.count)) - for item in reactions! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 4) != 0 {serializeInt32(reactionsCount!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(recentViewers!.count)) - for item in recentViewers! { - serializeInt64(item, buffer: buffer, boxed: false) - }} + serializeDouble(part, buffer: buffer, boxed: false) + serializeDouble(total, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .storyViews(let flags, let viewsCount, let forwardsCount, let reactions, let reactionsCount, let recentViewers): - return ("storyViews", [("flags", flags as Any), ("viewsCount", viewsCount as Any), ("forwardsCount", forwardsCount as Any), ("reactions", reactions as Any), ("reactionsCount", reactionsCount as Any), ("recentViewers", recentViewers as Any)]) + case .statsPercentValue(let part, let total): + return ("statsPercentValue", [("part", part as Any), ("total", total as Any)]) } } - public static func parse_storyViews(_ reader: BufferReader) -> StoryViews? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - if Int(_1!) & Int(1 << 2) != 0 {_3 = reader.readInt32() } - var _4: [Api.ReactionCount]? - if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ReactionCount.self) - } } - var _5: Int32? - if Int(_1!) & Int(1 << 4) != 0 {_5 = reader.readInt32() } - var _6: [Int64]? - if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) - } } + 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 - let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil - let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil - let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.StoryViews.storyViews(flags: _1!, viewsCount: _2!, forwardsCount: _3, reactions: _4, reactionsCount: _5, recentViewers: _6) + if _c1 && _c2 { + return Api.StatsPercentValue.statsPercentValue(part: _1!, total: _2!) } else { return nil @@ -677,43 +575,33 @@ public extension Api { } } public extension Api { - enum TextWithEntities: TypeConstructorDescription { - case textWithEntities(text: String, entities: [Api.MessageEntity]) + enum StatsURL: TypeConstructorDescription { + case statsURL(url: String) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .textWithEntities(let text, let entities): + case .statsURL(let url): if boxed { - buffer.appendInt32(1964978502) - } - serializeString(text, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities.count)) - for item in entities { - item.serialize(buffer, true) + buffer.appendInt32(1202287072) } + serializeString(url, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .textWithEntities(let text, let entities): - return ("textWithEntities", [("text", text as Any), ("entities", entities as Any)]) + case .statsURL(let url): + return ("statsURL", [("url", url as Any)]) } } - public static func parse_textWithEntities(_ reader: BufferReader) -> TextWithEntities? { + public static func parse_statsURL(_ reader: BufferReader) -> StatsURL? { var _1: String? _1 = parseString(reader) - var _2: [Api.MessageEntity]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) - } let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.TextWithEntities.textWithEntities(text: _1!, entities: _2!) + if _c1 { + return Api.StatsURL.statsURL(url: _1!) } else { return nil @@ -723,73 +611,43 @@ 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?) + enum StickerKeyword: TypeConstructorDescription { + case stickerKeyword(documentId: Int64, keyword: [String]) 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): + case .stickerKeyword(let documentId, let keyword): if boxed { - buffer.appendInt32(-1609668650) + 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) } - 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)]) + case .stickerKeyword(let documentId, let keyword): + return ("stickerKeyword", [("documentId", documentId as Any), ("keyword", keyword 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() } + 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 - 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) + if _c1 && _c2 { + return Api.StickerKeyword.stickerKeyword(documentId: _1!, keyword: _2!) } else { return nil @@ -799,63 +657,43 @@ public extension Api { } } public extension Api { - enum ThemeSettings: TypeConstructorDescription { - case themeSettings(flags: Int32, baseTheme: Api.BaseTheme, accentColor: Int32, outboxAccentColor: Int32?, messageColors: [Int32]?, wallpaper: Api.WallPaper?) + enum StickerPack: TypeConstructorDescription { + case stickerPack(emoticon: String, documents: [Int64]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .themeSettings(let flags, let baseTheme, let accentColor, let outboxAccentColor, let messageColors, let wallpaper): + case .stickerPack(let emoticon, let documents): if boxed { - buffer.appendInt32(-94849324) + buffer.appendInt32(313694676) + } + serializeString(emoticon, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(documents.count)) + for item in documents { + serializeInt64(item, buffer: buffer, boxed: false) } - 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)]) + case .stickerPack(let emoticon, let documents): + return ("stickerPack", [("emoticon", emoticon as Any), ("documents", documents 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 + public static func parse_stickerPack(_ reader: BufferReader) -> StickerPack? { + var _1: String? + _1 = parseString(reader) + var _2: [Int64]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) } - 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) + if _c1 && _c2 { + return Api.StickerPack.stickerPack(emoticon: _1!, documents: _2!) } else { return nil @@ -865,203 +703,277 @@ public extension Api { } } public extension Api { - enum TopPeer: TypeConstructorDescription { - case topPeer(peer: Api.Peer, rating: Double) + enum StickerSet: TypeConstructorDescription { + case stickerSet(flags: Int32, installedDate: Int32?, id: Int64, accessHash: Int64, title: String, shortName: String, thumbs: [Api.PhotoSize]?, thumbDcId: Int32?, thumbVersion: Int32?, thumbDocumentId: Int64?, count: Int32, hash: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .topPeer(let peer, let rating): + case .stickerSet(let flags, let installedDate, let id, let accessHash, let title, let shortName, let thumbs, let thumbDcId, let thumbVersion, let thumbDocumentId, let count, let hash): if boxed { - buffer.appendInt32(-305282981) + buffer.appendInt32(768691932) } - peer.serialize(buffer, true) - serializeDouble(rating, buffer: buffer, boxed: false) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(installedDate!, buffer: buffer, boxed: false)} + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + serializeString(shortName, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(thumbs!.count)) + for item in thumbs! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 4) != 0 {serializeInt32(thumbDcId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {serializeInt32(thumbVersion!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 8) != 0 {serializeInt64(thumbDocumentId!, buffer: buffer, boxed: false)} + serializeInt32(count, buffer: buffer, boxed: false) + serializeInt32(hash, 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)]) + case .stickerSet(let flags, let installedDate, let id, let accessHash, let title, let shortName, let thumbs, let thumbDcId, let thumbVersion, let thumbDocumentId, let count, let hash): + return ("stickerSet", [("flags", flags as Any), ("installedDate", installedDate as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("title", title as Any), ("shortName", shortName as Any), ("thumbs", thumbs as Any), ("thumbDcId", thumbDcId as Any), ("thumbVersion", thumbVersion as Any), ("thumbDocumentId", thumbDocumentId as Any), ("count", count as Any), ("hash", hash 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() + public static func parse_stickerSet(_ reader: BufferReader) -> StickerSet? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } + var _3: Int64? + _3 = reader.readInt64() + var _4: Int64? + _4 = reader.readInt64() + var _5: String? + _5 = parseString(reader) + var _6: String? + _6 = parseString(reader) + var _7: [Api.PhotoSize]? + if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() { + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PhotoSize.self) + } } + var _8: Int32? + if Int(_1!) & Int(1 << 4) != 0 {_8 = reader.readInt32() } + var _9: Int32? + if Int(_1!) & Int(1 << 4) != 0 {_9 = reader.readInt32() } + var _10: Int64? + if Int(_1!) & Int(1 << 8) != 0 {_10 = reader.readInt64() } + var _11: Int32? + _11 = reader.readInt32() + var _12: Int32? + _12 = reader.readInt32() let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.TopPeer.topPeer(peer: _1!, rating: _2!) - } - else { - return 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 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil + let _c8 = (Int(_1!) & Int(1 << 4) == 0) || _8 != nil + let _c9 = (Int(_1!) & Int(1 << 4) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 8) == 0) || _10 != nil + let _c11 = _11 != nil + let _c12 = _12 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 { + return Api.StickerSet.stickerSet(flags: _1!, installedDate: _2, id: _3!, accessHash: _4!, title: _5!, shortName: _6!, thumbs: _7, thumbDcId: _8, thumbVersion: _9, thumbDocumentId: _10, count: _11!, hash: _12!) + } + else { + return nil + } + } } } public extension Api { - enum TopPeerCategory: TypeConstructorDescription { - case topPeerCategoryBotsInline - case topPeerCategoryBotsPM - case topPeerCategoryChannels - case topPeerCategoryCorrespondents - case topPeerCategoryForwardChats - case topPeerCategoryForwardUsers - case topPeerCategoryGroups - case topPeerCategoryPhoneCalls + enum StickerSetCovered: TypeConstructorDescription { + case stickerSetCovered(set: Api.StickerSet, cover: Api.Document) + case stickerSetFullCovered(set: Api.StickerSet, packs: [Api.StickerPack], keywords: [Api.StickerKeyword], documents: [Api.Document]) + case stickerSetMultiCovered(set: Api.StickerSet, covers: [Api.Document]) + case stickerSetNoCovered(set: Api.StickerSet) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .topPeerCategoryBotsInline: + case .stickerSetCovered(let set, let cover): if boxed { - buffer.appendInt32(344356834) + buffer.appendInt32(1678812626) } - + set.serialize(buffer, true) + cover.serialize(buffer, true) break - case .topPeerCategoryBotsPM: + case .stickerSetFullCovered(let set, let packs, let keywords, let documents): if boxed { - buffer.appendInt32(-1419371685) + buffer.appendInt32(1087454222) } - - break - case .topPeerCategoryChannels: - if boxed { - buffer.appendInt32(371037736) + set.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(packs.count)) + for item in packs { + item.serialize(buffer, true) } - - break - case .topPeerCategoryCorrespondents: - if boxed { - buffer.appendInt32(104314861) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(keywords.count)) + for item in keywords { + item.serialize(buffer, true) } - - break - case .topPeerCategoryForwardChats: - if boxed { - buffer.appendInt32(-68239120) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(documents.count)) + for item in documents { + item.serialize(buffer, true) } - break - case .topPeerCategoryForwardUsers: + case .stickerSetMultiCovered(let set, let covers): if boxed { - buffer.appendInt32(-1472172887) + buffer.appendInt32(872932635) } - - break - case .topPeerCategoryGroups: - if boxed { - buffer.appendInt32(-1122524854) + set.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(covers.count)) + for item in covers { + item.serialize(buffer, true) } - break - case .topPeerCategoryPhoneCalls: + case .stickerSetNoCovered(let set): if boxed { - buffer.appendInt32(511092620) + buffer.appendInt32(2008112412) } - + set.serialize(buffer, true) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - 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", []) + case .stickerSetCovered(let set, let cover): + return ("stickerSetCovered", [("set", set as Any), ("cover", cover as Any)]) + case .stickerSetFullCovered(let set, let packs, let keywords, let documents): + return ("stickerSetFullCovered", [("set", set as Any), ("packs", packs as Any), ("keywords", keywords as Any), ("documents", documents as Any)]) + case .stickerSetMultiCovered(let set, let covers): + return ("stickerSetMultiCovered", [("set", set as Any), ("covers", covers as Any)]) + case .stickerSetNoCovered(let set): + return ("stickerSetNoCovered", [("set", set as Any)]) } } - 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_stickerSetCovered(_ reader: BufferReader) -> StickerSetCovered? { + var _1: Api.StickerSet? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.StickerSet + } + var _2: Api.Document? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Document + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.StickerSetCovered.stickerSetCovered(set: _1!, cover: _2!) + } + else { + return nil + } } - public static func parse_topPeerCategoryForwardUsers(_ reader: BufferReader) -> TopPeerCategory? { - return Api.TopPeerCategory.topPeerCategoryForwardUsers + public static func parse_stickerSetFullCovered(_ reader: BufferReader) -> StickerSetCovered? { + var _1: Api.StickerSet? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.StickerSet + } + var _2: [Api.StickerPack]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self) + } + var _3: [Api.StickerKeyword]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerKeyword.self) + } + var _4: [Api.Document]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.StickerSetCovered.stickerSetFullCovered(set: _1!, packs: _2!, keywords: _3!, documents: _4!) + } + else { + return nil + } } - public static func parse_topPeerCategoryGroups(_ reader: BufferReader) -> TopPeerCategory? { - return Api.TopPeerCategory.topPeerCategoryGroups + public static func parse_stickerSetMultiCovered(_ reader: BufferReader) -> StickerSetCovered? { + var _1: Api.StickerSet? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.StickerSet + } + 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.StickerSetCovered.stickerSetMultiCovered(set: _1!, covers: _2!) + } + else { + return nil + } } - public static func parse_topPeerCategoryPhoneCalls(_ reader: BufferReader) -> TopPeerCategory? { - return Api.TopPeerCategory.topPeerCategoryPhoneCalls + public static func parse_stickerSetNoCovered(_ reader: BufferReader) -> StickerSetCovered? { + var _1: Api.StickerSet? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.StickerSet + } + let _c1 = _1 != nil + if _c1 { + return Api.StickerSetCovered.stickerSetNoCovered(set: _1!) + } + else { + return nil + } } } } public extension Api { - enum TopPeerCategoryPeers: TypeConstructorDescription { - case topPeerCategoryPeers(category: Api.TopPeerCategory, count: Int32, peers: [Api.TopPeer]) + enum StoriesStealthMode: TypeConstructorDescription { + case storiesStealthMode(flags: Int32, activeUntilDate: Int32?, cooldownUntilDate: Int32?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .topPeerCategoryPeers(let category, let count, let peers): + case .storiesStealthMode(let flags, let activeUntilDate, let cooldownUntilDate): if boxed { - buffer.appendInt32(-75283823) - } - category.serialize(buffer, true) - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(peers.count)) - for item in peers { - item.serialize(buffer, true) + buffer.appendInt32(1898850301) } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(activeUntilDate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(cooldownUntilDate!, buffer: buffer, boxed: false)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .topPeerCategoryPeers(let category, let count, let peers): - return ("topPeerCategoryPeers", [("category", category as Any), ("count", count as Any), ("peers", peers as Any)]) + case .storiesStealthMode(let flags, let activeUntilDate, let cooldownUntilDate): + return ("storiesStealthMode", [("flags", flags as Any), ("activeUntilDate", activeUntilDate as Any), ("cooldownUntilDate", cooldownUntilDate as Any)]) } } - public static func parse_topPeerCategoryPeers(_ reader: BufferReader) -> TopPeerCategoryPeers? { - var _1: Api.TopPeerCategory? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.TopPeerCategory - } + public static func parse_storiesStealthMode(_ reader: BufferReader) -> StoriesStealthMode? { + var _1: Int32? + _1 = reader.readInt32() var _2: Int32? - _2 = reader.readInt32() - var _3: [Api.TopPeer]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.TopPeer.self) - } + if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } + var _3: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() } let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil if _c1 && _c2 && _c3 { - return Api.TopPeerCategoryPeers.topPeerCategoryPeers(category: _1!, count: _2!, peers: _3!) + return Api.StoriesStealthMode.storiesStealthMode(flags: _1!, activeUntilDate: _2, cooldownUntilDate: _3) } else { return nil @@ -1071,3035 +983,193 @@ public extension Api { } } public extension Api { - indirect enum Update: TypeConstructorDescription { - case updateAttachMenuBots - case updateAutoSaveSettings - case updateBotCallbackQuery(flags: Int32, queryId: Int64, userId: Int64, peer: Api.Peer, msgId: Int32, chatInstance: Int64, data: Buffer?, gameShortName: String?) - case updateBotChatInviteRequester(peer: Api.Peer, date: Int32, userId: Int64, about: String, invite: Api.ExportedChatInvite, qts: Int32) - case updateBotCommands(peer: Api.Peer, botId: Int64, commands: [Api.BotCommand]) - case updateBotInlineQuery(flags: Int32, queryId: Int64, userId: Int64, query: String, geo: Api.GeoPoint?, peerType: Api.InlineQueryPeerType?, offset: String) - case updateBotInlineSend(flags: Int32, userId: Int64, query: String, geo: Api.GeoPoint?, id: String, msgId: Api.InputBotInlineMessageID?) - case updateBotMenuButton(botId: Int64, button: Api.BotMenuButton) - case updateBotPrecheckoutQuery(flags: Int32, queryId: Int64, userId: Int64, payload: Buffer, info: Api.PaymentRequestedInfo?, shippingOptionId: String?, currency: String, totalAmount: Int64) - case updateBotShippingQuery(queryId: Int64, userId: Int64, payload: Buffer, shippingAddress: Api.PostAddress) - case updateBotStopped(userId: Int64, date: Int32, stopped: Api.Bool, qts: Int32) - case updateBotWebhookJSON(data: Api.DataJSON) - case updateBotWebhookJSONQuery(queryId: Int64, data: Api.DataJSON, timeout: Int32) - case updateChannel(channelId: Int64) - case updateChannelAvailableMessages(channelId: Int64, availableMinId: Int32) - case updateChannelMessageForwards(channelId: Int64, id: Int32, forwards: Int32) - case updateChannelMessageViews(channelId: Int64, id: Int32, views: Int32) - case updateChannelParticipant(flags: Int32, channelId: Int64, date: Int32, actorId: Int64, userId: Int64, prevParticipant: Api.ChannelParticipant?, newParticipant: Api.ChannelParticipant?, invite: Api.ExportedChatInvite?, qts: Int32) - case updateChannelPinnedTopic(flags: Int32, channelId: Int64, topicId: Int32) - case updateChannelPinnedTopics(flags: Int32, channelId: Int64, order: [Int32]?) - case updateChannelReadMessagesContents(flags: Int32, channelId: Int64, topMsgId: Int32?, messages: [Int32]) - case updateChannelTooLong(flags: Int32, channelId: Int64, pts: Int32?) - case updateChannelUserTyping(flags: Int32, channelId: Int64, topMsgId: Int32?, fromId: Api.Peer, action: Api.SendMessageAction) - case updateChannelWebPage(channelId: Int64, webpage: Api.WebPage, pts: Int32, ptsCount: Int32) - case updateChat(chatId: Int64) - case updateChatDefaultBannedRights(peer: Api.Peer, defaultBannedRights: Api.ChatBannedRights, version: Int32) - case updateChatParticipant(flags: Int32, chatId: Int64, date: Int32, actorId: Int64, userId: Int64, prevParticipant: Api.ChatParticipant?, newParticipant: Api.ChatParticipant?, invite: Api.ExportedChatInvite?, qts: Int32) - case updateChatParticipantAdd(chatId: Int64, userId: Int64, inviterId: Int64, date: Int32, version: Int32) - case updateChatParticipantAdmin(chatId: Int64, userId: Int64, isAdmin: Api.Bool, version: Int32) - case updateChatParticipantDelete(chatId: Int64, userId: Int64, version: Int32) - case updateChatParticipants(participants: Api.ChatParticipants) - case updateChatUserTyping(chatId: Int64, fromId: Api.Peer, action: Api.SendMessageAction) - case updateConfig - case updateContactsReset - case updateDcOptions(dcOptions: [Api.DcOption]) - case updateDeleteChannelMessages(channelId: Int64, messages: [Int32], pts: Int32, ptsCount: Int32) - case updateDeleteMessages(messages: [Int32], pts: Int32, ptsCount: Int32) - case updateDeleteScheduledMessages(peer: Api.Peer, messages: [Int32]) - case updateDialogFilter(flags: Int32, id: Int32, filter: Api.DialogFilter?) - case updateDialogFilterOrder(order: [Int32]) - case updateDialogFilters - case updateDialogPinned(flags: Int32, folderId: Int32?, peer: Api.DialogPeer) - case updateDialogUnreadMark(flags: Int32, peer: Api.DialogPeer) - case updateDraftMessage(flags: Int32, peer: Api.Peer, topMsgId: Int32?, draft: Api.DraftMessage) - case updateEditChannelMessage(message: Api.Message, pts: Int32, ptsCount: Int32) - case updateEditMessage(message: Api.Message, pts: Int32, ptsCount: Int32) - case updateEncryptedChatTyping(chatId: Int32) - case updateEncryptedMessagesRead(chatId: Int32, maxDate: Int32, date: Int32) - case updateEncryption(chat: Api.EncryptedChat, date: Int32) - case updateFavedStickers - case updateFolderPeers(folderPeers: [Api.FolderPeer], pts: Int32, ptsCount: Int32) - case updateGeoLiveViewed(peer: Api.Peer, msgId: Int32) - case updateGroupCall(chatId: Int64, call: Api.GroupCall) - case updateGroupCallConnection(flags: Int32, params: Api.DataJSON) - case updateGroupCallParticipants(call: Api.InputGroupCall, participants: [Api.GroupCallParticipant], version: Int32) - case updateGroupInvitePrivacyForbidden(userId: Int64) - case updateInlineBotCallbackQuery(flags: Int32, queryId: Int64, userId: Int64, msgId: Api.InputBotInlineMessageID, chatInstance: Int64, data: Buffer?, gameShortName: String?) - case updateLangPack(difference: Api.LangPackDifference) - case updateLangPackTooLong(langCode: String) - case updateLoginToken - case updateMessageExtendedMedia(peer: Api.Peer, msgId: Int32, extendedMedia: Api.MessageExtendedMedia) - case updateMessageID(id: Int32, randomId: Int64) - case updateMessagePoll(flags: Int32, pollId: Int64, poll: Api.Poll?, results: Api.PollResults) - case updateMessagePollVote(pollId: Int64, peer: Api.Peer, options: [Buffer], qts: Int32) - case updateMessageReactions(flags: Int32, peer: Api.Peer, msgId: Int32, topMsgId: Int32?, reactions: Api.MessageReactions) - case updateMoveStickerSetToTop(flags: Int32, stickerset: Int64) - case updateNewAuthorization(flags: Int32, hash: Int64, date: Int32?, device: String?, location: String?) - case updateNewChannelMessage(message: Api.Message, pts: Int32, ptsCount: Int32) - case updateNewEncryptedMessage(message: Api.EncryptedMessage, qts: Int32) - case updateNewMessage(message: Api.Message, pts: Int32, ptsCount: Int32) - case updateNewScheduledMessage(message: Api.Message) - case updateNewStickerSet(stickerset: Api.messages.StickerSet) - case updateNotifySettings(peer: Api.NotifyPeer, notifySettings: Api.PeerNotifySettings) - case updatePeerBlocked(flags: Int32, peerId: Api.Peer) - case updatePeerHistoryTTL(flags: Int32, peer: Api.Peer, ttlPeriod: Int32?) - case updatePeerLocated(peers: [Api.PeerLocated]) - case updatePeerSettings(peer: Api.Peer, settings: Api.PeerSettings) - case updatePendingJoinRequests(peer: Api.Peer, requestsPending: Int32, recentRequesters: [Int64]) - case updatePhoneCall(phoneCall: Api.PhoneCall) - case updatePhoneCallSignalingData(phoneCallId: Int64, data: Buffer) - case updatePinnedChannelMessages(flags: Int32, channelId: Int64, messages: [Int32], pts: Int32, ptsCount: Int32) - case updatePinnedDialogs(flags: Int32, folderId: Int32?, order: [Api.DialogPeer]?) - case updatePinnedMessages(flags: Int32, peer: Api.Peer, messages: [Int32], pts: Int32, ptsCount: Int32) - case updatePrivacy(key: Api.PrivacyKey, rules: [Api.PrivacyRule]) - case updatePtsChanged - case updateReadChannelDiscussionInbox(flags: Int32, channelId: Int64, topMsgId: Int32, readMaxId: Int32, broadcastId: Int64?, broadcastPost: Int32?) - case updateReadChannelDiscussionOutbox(channelId: Int64, topMsgId: Int32, readMaxId: Int32) - case updateReadChannelInbox(flags: Int32, folderId: Int32?, channelId: Int64, maxId: Int32, stillUnreadCount: Int32, pts: Int32) - case updateReadChannelOutbox(channelId: Int64, maxId: Int32) - case updateReadFeaturedEmojiStickers - case updateReadFeaturedStickers - case updateReadHistoryInbox(flags: Int32, folderId: Int32?, peer: Api.Peer, maxId: Int32, stillUnreadCount: Int32, pts: Int32, ptsCount: Int32) - case updateReadHistoryOutbox(peer: Api.Peer, maxId: Int32, pts: Int32, ptsCount: Int32) - case updateReadMessagesContents(flags: Int32, messages: [Int32], pts: Int32, ptsCount: Int32, date: Int32?) - case updateReadStories(peer: Api.Peer, maxId: Int32) - case updateRecentEmojiStatuses - case updateRecentReactions - case updateRecentStickers - case updateSavedGifs - case updateSavedRingtones - case updateSentStoryReaction(peer: Api.Peer, storyId: Int32, reaction: Api.Reaction) - case updateServiceNotification(flags: Int32, inboxDate: Int32?, type: String, message: String, media: Api.MessageMedia, entities: [Api.MessageEntity]) - case updateStickerSets(flags: Int32) - case updateStickerSetsOrder(flags: Int32, order: [Int64]) - case updateStoriesStealthMode(stealthMode: Api.StoriesStealthMode) - case updateStory(peer: Api.Peer, story: Api.StoryItem) - case updateStoryID(id: Int32, randomId: Int64) - case updateTheme(theme: Api.Theme) - case updateTranscribedAudio(flags: Int32, peer: Api.Peer, msgId: Int32, transcriptionId: Int64, text: String) - case updateUser(userId: Int64) - case updateUserEmojiStatus(userId: Int64, emojiStatus: Api.EmojiStatus) - case updateUserName(userId: Int64, firstName: String, lastName: String, usernames: [Api.Username]) - case updateUserPhone(userId: Int64, phone: String) - case updateUserStatus(userId: Int64, status: Api.UserStatus) - case updateUserTyping(userId: Int64, action: Api.SendMessageAction) - case updateWebPage(webpage: Api.WebPage, pts: Int32, ptsCount: Int32) - case updateWebViewResultSent(queryId: Int64) + enum StoryFwdHeader: TypeConstructorDescription { + case storyFwdHeader(flags: Int32, from: Api.Peer?, fromName: String?, storyId: Int32?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .updateAttachMenuBots: - if boxed { - buffer.appendInt32(397910539) - } - - break - case .updateAutoSaveSettings: - if boxed { - buffer.appendInt32(-335171433) - } - - break - case .updateBotCallbackQuery(let flags, let queryId, let userId, let peer, let msgId, let chatInstance, let data, let gameShortName): + case .storyFwdHeader(let flags, let from, let fromName, let storyId): if boxed { - buffer.appendInt32(-1177566067) + buffer.appendInt32(-1205411504) } serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(queryId, buffer: buffer, boxed: false) - serializeInt64(userId, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - serializeInt64(chatInstance, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeBytes(data!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeString(gameShortName!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {from!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(fromName!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeInt32(storyId!, buffer: buffer, boxed: false)} break - case .updateBotChatInviteRequester(let peer, let date, let userId, let about, let invite, let qts): + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .storyFwdHeader(let flags, let from, let fromName, let storyId): + return ("storyFwdHeader", [("flags", flags as Any), ("from", from as Any), ("fromName", fromName as Any), ("storyId", storyId as Any)]) + } + } + + public static func parse_storyFwdHeader(_ reader: BufferReader) -> StoryFwdHeader? { + 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: String? + if Int(_1!) & Int(1 << 1) != 0 {_3 = parseString(reader) } + var _4: Int32? + if Int(_1!) & Int(1 << 2) != 0 {_4 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.StoryFwdHeader.storyFwdHeader(flags: _1!, from: _2, fromName: _3, storyId: _4) + } + else { + return nil + } + } + + } +} +public extension Api { + indirect enum StoryItem: TypeConstructorDescription { + case storyItem(flags: Int32, id: Int32, date: Int32, fwdFrom: Api.StoryFwdHeader?, expireDate: Int32, caption: String?, entities: [Api.MessageEntity]?, media: Api.MessageMedia, mediaAreas: [Api.MediaArea]?, privacy: [Api.PrivacyRule]?, views: Api.StoryViews?, sentReaction: Api.Reaction?) + case storyItemDeleted(id: Int32) + case storyItemSkipped(flags: Int32, id: Int32, date: Int32, expireDate: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .storyItem(let flags, let id, let date, let fwdFrom, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction): if boxed { - buffer.appendInt32(299870598) + buffer.appendInt32(-1352440415) } - peer.serialize(buffer, true) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(id, buffer: buffer, boxed: false) serializeInt32(date, buffer: buffer, boxed: false) - serializeInt64(userId, buffer: buffer, boxed: false) - serializeString(about, buffer: buffer, boxed: false) - invite.serialize(buffer, true) - serializeInt32(qts, buffer: buffer, boxed: false) - break - case .updateBotCommands(let peer, let botId, let commands): - if boxed { - buffer.appendInt32(1299263278) - } - peer.serialize(buffer, true) - serializeInt64(botId, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(commands.count)) - for item in commands { + if Int(flags) & Int(1 << 17) != 0 {fwdFrom!.serialize(buffer, true)} + serializeInt32(expireDate, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(caption!, 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) - } + }} + media.serialize(buffer, true) + if Int(flags) & Int(1 << 14) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(mediaAreas!.count)) + for item in mediaAreas! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 2) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(privacy!.count)) + for item in privacy! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 3) != 0 {views!.serialize(buffer, true)} + if Int(flags) & Int(1 << 15) != 0 {sentReaction!.serialize(buffer, true)} break - case .updateBotInlineQuery(let flags, let queryId, let userId, let query, let geo, let peerType, let offset): + case .storyItemDeleted(let id): if boxed { - buffer.appendInt32(1232025500) + buffer.appendInt32(1374088783) } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(queryId, buffer: buffer, boxed: false) - serializeInt64(userId, buffer: buffer, boxed: false) - serializeString(query, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {geo!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {peerType!.serialize(buffer, true)} - serializeString(offset, buffer: buffer, boxed: false) + serializeInt32(id, buffer: buffer, boxed: false) break - case .updateBotInlineSend(let flags, let userId, let query, let geo, let id, let msgId): + case .storyItemSkipped(let flags, let id, let date, let expireDate): if boxed { - buffer.appendInt32(317794823) + buffer.appendInt32(-5388013) } serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(userId, buffer: buffer, boxed: false) - serializeString(query, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {geo!.serialize(buffer, true)} - serializeString(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {msgId!.serialize(buffer, true)} - break - case .updateBotMenuButton(let botId, let button): - if boxed { - buffer.appendInt32(347625491) - } - serializeInt64(botId, buffer: buffer, boxed: false) - button.serialize(buffer, true) - break - case .updateBotPrecheckoutQuery(let flags, let queryId, let userId, let payload, let info, let shippingOptionId, let currency, let totalAmount): - if boxed { - buffer.appendInt32(-1934976362) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(queryId, buffer: buffer, boxed: false) - serializeInt64(userId, buffer: buffer, boxed: false) - serializeBytes(payload, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {info!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {serializeString(shippingOptionId!, buffer: buffer, boxed: false)} - serializeString(currency, buffer: buffer, boxed: false) - serializeInt64(totalAmount, buffer: buffer, boxed: false) - break - case .updateBotShippingQuery(let queryId, let userId, let payload, let shippingAddress): - if boxed { - buffer.appendInt32(-1246823043) - } - serializeInt64(queryId, buffer: buffer, boxed: false) - serializeInt64(userId, buffer: buffer, boxed: false) - serializeBytes(payload, buffer: buffer, boxed: false) - shippingAddress.serialize(buffer, true) - break - case .updateBotStopped(let userId, let date, let stopped, let qts): - if boxed { - buffer.appendInt32(-997782967) - } - serializeInt64(userId, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - stopped.serialize(buffer, true) - serializeInt32(qts, buffer: buffer, boxed: false) - break - case .updateBotWebhookJSON(let data): - if boxed { - buffer.appendInt32(-2095595325) - } - data.serialize(buffer, true) - break - case .updateBotWebhookJSONQuery(let queryId, let data, let timeout): - if boxed { - buffer.appendInt32(-1684914010) - } - serializeInt64(queryId, buffer: buffer, boxed: false) - data.serialize(buffer, true) - serializeInt32(timeout, buffer: buffer, boxed: false) - break - case .updateChannel(let channelId): - if boxed { - buffer.appendInt32(1666927625) - } - serializeInt64(channelId, buffer: buffer, boxed: false) - break - case .updateChannelAvailableMessages(let channelId, let availableMinId): - if boxed { - buffer.appendInt32(-1304443240) - } - serializeInt64(channelId, buffer: buffer, boxed: false) - serializeInt32(availableMinId, buffer: buffer, boxed: false) - break - case .updateChannelMessageForwards(let channelId, let id, let forwards): - if boxed { - buffer.appendInt32(-761649164) - } - serializeInt64(channelId, buffer: buffer, boxed: false) - serializeInt32(id, buffer: buffer, boxed: false) - serializeInt32(forwards, buffer: buffer, boxed: false) - break - case .updateChannelMessageViews(let channelId, let id, let views): - if boxed { - buffer.appendInt32(-232346616) - } - serializeInt64(channelId, buffer: buffer, boxed: false) - serializeInt32(id, buffer: buffer, boxed: false) - serializeInt32(views, buffer: buffer, boxed: false) - break - case .updateChannelParticipant(let flags, let channelId, let date, let actorId, let userId, let prevParticipant, let newParticipant, let invite, let qts): - if boxed { - buffer.appendInt32(-1738720581) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(channelId, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt64(actorId, buffer: buffer, boxed: false) - serializeInt64(userId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {prevParticipant!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {newParticipant!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {invite!.serialize(buffer, true)} - serializeInt32(qts, buffer: buffer, boxed: false) - break - case .updateChannelPinnedTopic(let flags, let channelId, let topicId): - if boxed { - buffer.appendInt32(422509539) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(channelId, buffer: buffer, boxed: false) - serializeInt32(topicId, buffer: buffer, boxed: false) - break - case .updateChannelPinnedTopics(let flags, let channelId, let order): - if boxed { - buffer.appendInt32(-31881726) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(channelId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(order!.count)) - for item in order! { - serializeInt32(item, buffer: buffer, boxed: false) - }} - break - case .updateChannelReadMessagesContents(let flags, let channelId, let topMsgId, let messages): - if boxed { - buffer.appendInt32(-366410403) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(channelId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - serializeInt32(item, buffer: buffer, boxed: false) - } - break - case .updateChannelTooLong(let flags, let channelId, let pts): - if boxed { - buffer.appendInt32(277713951) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(channelId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(pts!, buffer: buffer, boxed: false)} - break - case .updateChannelUserTyping(let flags, let channelId, let topMsgId, let fromId, let action): - if boxed { - buffer.appendInt32(-1937192669) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(channelId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} - fromId.serialize(buffer, true) - action.serialize(buffer, true) - break - case .updateChannelWebPage(let channelId, let webpage, let pts, let ptsCount): - if boxed { - buffer.appendInt32(791390623) - } - serializeInt64(channelId, buffer: buffer, boxed: false) - webpage.serialize(buffer, true) - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - break - case .updateChat(let chatId): - if boxed { - buffer.appendInt32(-124097970) - } - serializeInt64(chatId, buffer: buffer, boxed: false) - break - case .updateChatDefaultBannedRights(let peer, let defaultBannedRights, let version): - if boxed { - buffer.appendInt32(1421875280) - } - peer.serialize(buffer, true) - defaultBannedRights.serialize(buffer, true) - serializeInt32(version, buffer: buffer, boxed: false) - break - case .updateChatParticipant(let flags, let chatId, let date, let actorId, let userId, let prevParticipant, let newParticipant, let invite, let qts): - if boxed { - buffer.appendInt32(-796432838) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(chatId, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt64(actorId, buffer: buffer, boxed: false) - serializeInt64(userId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {prevParticipant!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {newParticipant!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {invite!.serialize(buffer, true)} - serializeInt32(qts, buffer: buffer, boxed: false) - break - case .updateChatParticipantAdd(let chatId, let userId, let inviterId, let date, let version): - if boxed { - buffer.appendInt32(1037718609) - } - serializeInt64(chatId, buffer: buffer, boxed: false) - serializeInt64(userId, buffer: buffer, boxed: false) - serializeInt64(inviterId, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt32(version, buffer: buffer, boxed: false) - break - case .updateChatParticipantAdmin(let chatId, let userId, let isAdmin, let version): - if boxed { - buffer.appendInt32(-674602590) - } - serializeInt64(chatId, buffer: buffer, boxed: false) - serializeInt64(userId, buffer: buffer, boxed: false) - isAdmin.serialize(buffer, true) - serializeInt32(version, buffer: buffer, boxed: false) - break - case .updateChatParticipantDelete(let chatId, let userId, let version): - if boxed { - buffer.appendInt32(-483443337) - } - serializeInt64(chatId, buffer: buffer, boxed: false) - serializeInt64(userId, buffer: buffer, boxed: false) - serializeInt32(version, buffer: buffer, boxed: false) - break - case .updateChatParticipants(let participants): - if boxed { - buffer.appendInt32(125178264) - } - participants.serialize(buffer, true) - break - case .updateChatUserTyping(let chatId, let fromId, let action): - if boxed { - buffer.appendInt32(-2092401936) - } - serializeInt64(chatId, buffer: buffer, boxed: false) - fromId.serialize(buffer, true) - action.serialize(buffer, true) - break - case .updateConfig: - if boxed { - buffer.appendInt32(-1574314746) - } - - break - case .updateContactsReset: - if boxed { - buffer.appendInt32(1887741886) - } - - break - case .updateDcOptions(let dcOptions): - if boxed { - buffer.appendInt32(-1906403213) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(dcOptions.count)) - for item in dcOptions { - item.serialize(buffer, true) - } - break - case .updateDeleteChannelMessages(let channelId, let messages, let pts, let ptsCount): - if boxed { - buffer.appendInt32(-1020437742) - } - serializeInt64(channelId, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - serializeInt32(item, buffer: buffer, boxed: false) - } - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - break - case .updateDeleteMessages(let messages, let pts, let ptsCount): - if boxed { - buffer.appendInt32(-1576161051) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - serializeInt32(item, buffer: buffer, boxed: false) - } - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - break - case .updateDeleteScheduledMessages(let peer, let messages): - if boxed { - buffer.appendInt32(-1870238482) - } - peer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - serializeInt32(item, buffer: buffer, boxed: false) - } - break - case .updateDialogFilter(let flags, let id, let filter): - if boxed { - buffer.appendInt32(654302845) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {filter!.serialize(buffer, true)} - break - case .updateDialogFilterOrder(let order): - if boxed { - buffer.appendInt32(-1512627963) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(order.count)) - for item in order { - serializeInt32(item, buffer: buffer, boxed: false) - } - break - case .updateDialogFilters: - if boxed { - buffer.appendInt32(889491791) - } - - break - case .updateDialogPinned(let flags, let folderId, let peer): - if boxed { - buffer.appendInt32(1852826908) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} - peer.serialize(buffer, true) - break - case .updateDialogUnreadMark(let flags, let peer): - if boxed { - buffer.appendInt32(-513517117) - } - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - break - case .updateDraftMessage(let flags, let peer, let topMsgId, let draft): - if boxed { - buffer.appendInt32(457829485) - } - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} - draft.serialize(buffer, true) - break - case .updateEditChannelMessage(let message, let pts, let ptsCount): - if boxed { - buffer.appendInt32(457133559) - } - message.serialize(buffer, true) - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - break - case .updateEditMessage(let message, let pts, let ptsCount): - if boxed { - buffer.appendInt32(-469536605) - } - message.serialize(buffer, true) - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - break - case .updateEncryptedChatTyping(let chatId): - if boxed { - buffer.appendInt32(386986326) - } - serializeInt32(chatId, buffer: buffer, boxed: false) - break - case .updateEncryptedMessagesRead(let chatId, let maxDate, let date): - if boxed { - buffer.appendInt32(956179895) - } - serializeInt32(chatId, buffer: buffer, boxed: false) - serializeInt32(maxDate, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - break - case .updateEncryption(let chat, let date): - if boxed { - buffer.appendInt32(-1264392051) - } - chat.serialize(buffer, true) - serializeInt32(date, buffer: buffer, boxed: false) - break - case .updateFavedStickers: - if boxed { - buffer.appendInt32(-451831443) - } - - break - case .updateFolderPeers(let folderPeers, let pts, let ptsCount): - if boxed { - buffer.appendInt32(422972864) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(folderPeers.count)) - for item in folderPeers { - item.serialize(buffer, true) - } - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - break - case .updateGeoLiveViewed(let peer, let msgId): - if boxed { - buffer.appendInt32(-2027964103) - } - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - break - case .updateGroupCall(let chatId, let call): - if boxed { - buffer.appendInt32(347227392) - } - serializeInt64(chatId, buffer: buffer, boxed: false) - call.serialize(buffer, true) - break - case .updateGroupCallConnection(let flags, let params): - if boxed { - buffer.appendInt32(192428418) - } - serializeInt32(flags, buffer: buffer, boxed: false) - params.serialize(buffer, true) - break - case .updateGroupCallParticipants(let call, let participants, let version): - if boxed { - buffer.appendInt32(-219423922) - } - call.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(participants.count)) - for item in participants { - item.serialize(buffer, true) - } - serializeInt32(version, buffer: buffer, boxed: false) - break - case .updateGroupInvitePrivacyForbidden(let userId): - if boxed { - buffer.appendInt32(-856651050) - } - serializeInt64(userId, buffer: buffer, boxed: false) - break - case .updateInlineBotCallbackQuery(let flags, let queryId, let userId, let msgId, let chatInstance, let data, let gameShortName): - if boxed { - buffer.appendInt32(1763610706) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(queryId, buffer: buffer, boxed: false) - serializeInt64(userId, buffer: buffer, boxed: false) - msgId.serialize(buffer, true) - serializeInt64(chatInstance, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeBytes(data!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeString(gameShortName!, buffer: buffer, boxed: false)} - break - case .updateLangPack(let difference): - if boxed { - buffer.appendInt32(1442983757) - } - difference.serialize(buffer, true) - break - case .updateLangPackTooLong(let langCode): - if boxed { - buffer.appendInt32(1180041828) - } - serializeString(langCode, buffer: buffer, boxed: false) - break - case .updateLoginToken: - if boxed { - buffer.appendInt32(1448076945) - } - - break - case .updateMessageExtendedMedia(let peer, let msgId, let extendedMedia): - if boxed { - buffer.appendInt32(1517529484) - } - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - extendedMedia.serialize(buffer, true) - break - case .updateMessageID(let id, let randomId): - if boxed { - buffer.appendInt32(1318109142) - } - serializeInt32(id, buffer: buffer, boxed: false) - serializeInt64(randomId, buffer: buffer, boxed: false) - break - case .updateMessagePoll(let flags, let pollId, let poll, let results): - if boxed { - buffer.appendInt32(-1398708869) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(pollId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {poll!.serialize(buffer, true)} - results.serialize(buffer, true) - break - case .updateMessagePollVote(let pollId, let peer, let options, let qts): - if boxed { - buffer.appendInt32(619974263) - } - serializeInt64(pollId, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(options.count)) - for item in options { - serializeBytes(item, buffer: buffer, boxed: false) - } - serializeInt32(qts, buffer: buffer, boxed: false) - break - case .updateMessageReactions(let flags, let peer, let msgId, let topMsgId, let reactions): - if boxed { - buffer.appendInt32(1578843320) - } - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} - reactions.serialize(buffer, true) - break - case .updateMoveStickerSetToTop(let flags, let stickerset): - if boxed { - buffer.appendInt32(-2030252155) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(stickerset, buffer: buffer, boxed: false) - break - case .updateNewAuthorization(let flags, let hash, let date, let device, let location): - if boxed { - buffer.appendInt32(-1991136273) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(date!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 0) != 0 {serializeString(device!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 0) != 0 {serializeString(location!, buffer: buffer, boxed: false)} - break - case .updateNewChannelMessage(let message, let pts, let ptsCount): - if boxed { - buffer.appendInt32(1656358105) - } - message.serialize(buffer, true) - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - break - case .updateNewEncryptedMessage(let message, let qts): - if boxed { - buffer.appendInt32(314359194) - } - message.serialize(buffer, true) - serializeInt32(qts, buffer: buffer, boxed: false) - break - case .updateNewMessage(let message, let pts, let ptsCount): - if boxed { - buffer.appendInt32(522914557) - } - message.serialize(buffer, true) - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - break - case .updateNewScheduledMessage(let message): - if boxed { - buffer.appendInt32(967122427) - } - message.serialize(buffer, true) - break - case .updateNewStickerSet(let stickerset): - if boxed { - buffer.appendInt32(1753886890) - } - stickerset.serialize(buffer, true) - break - case .updateNotifySettings(let peer, let notifySettings): - if boxed { - buffer.appendInt32(-1094555409) - } - peer.serialize(buffer, true) - notifySettings.serialize(buffer, true) - break - case .updatePeerBlocked(let flags, let peerId): - if boxed { - buffer.appendInt32(-337610926) - } - serializeInt32(flags, buffer: buffer, boxed: false) - peerId.serialize(buffer, true) - break - case .updatePeerHistoryTTL(let flags, let peer, let ttlPeriod): - if boxed { - buffer.appendInt32(-1147422299) - } - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)} - break - case .updatePeerLocated(let peers): - if boxed { - buffer.appendInt32(-1263546448) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(peers.count)) - for item in peers { - item.serialize(buffer, true) - } - break - case .updatePeerSettings(let peer, let settings): - if boxed { - buffer.appendInt32(1786671974) - } - peer.serialize(buffer, true) - settings.serialize(buffer, true) - break - case .updatePendingJoinRequests(let peer, let requestsPending, let recentRequesters): - if boxed { - buffer.appendInt32(1885586395) - } - peer.serialize(buffer, true) - serializeInt32(requestsPending, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(recentRequesters.count)) - for item in recentRequesters { - serializeInt64(item, buffer: buffer, boxed: false) - } - break - case .updatePhoneCall(let phoneCall): - if boxed { - buffer.appendInt32(-1425052898) - } - phoneCall.serialize(buffer, true) - break - case .updatePhoneCallSignalingData(let phoneCallId, let data): - if boxed { - buffer.appendInt32(643940105) - } - serializeInt64(phoneCallId, buffer: buffer, boxed: false) - serializeBytes(data, buffer: buffer, boxed: false) - break - case .updatePinnedChannelMessages(let flags, let channelId, let messages, let pts, let ptsCount): - if boxed { - buffer.appendInt32(1538885128) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(channelId, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - serializeInt32(item, buffer: buffer, boxed: false) - } - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - break - case .updatePinnedDialogs(let flags, let folderId, let order): - if boxed { - buffer.appendInt32(-99664734) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(order!.count)) - for item in order! { - item.serialize(buffer, true) - }} - break - case .updatePinnedMessages(let flags, let peer, let messages, let pts, let ptsCount): - if boxed { - buffer.appendInt32(-309990731) - } - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - serializeInt32(item, buffer: buffer, boxed: false) - } - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - break - case .updatePrivacy(let key, let rules): - if boxed { - buffer.appendInt32(-298113238) - } - key.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(rules.count)) - for item in rules { - item.serialize(buffer, true) - } - break - case .updatePtsChanged: - if boxed { - buffer.appendInt32(861169551) - } - - break - case .updateReadChannelDiscussionInbox(let flags, let channelId, let topMsgId, let readMaxId, let broadcastId, let broadcastPost): - if boxed { - buffer.appendInt32(-693004986) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(channelId, buffer: buffer, boxed: false) - serializeInt32(topMsgId, buffer: buffer, boxed: false) - serializeInt32(readMaxId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt64(broadcastId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(broadcastPost!, buffer: buffer, boxed: false)} - break - case .updateReadChannelDiscussionOutbox(let channelId, let topMsgId, let readMaxId): - if boxed { - buffer.appendInt32(1767677564) - } - serializeInt64(channelId, buffer: buffer, boxed: false) - serializeInt32(topMsgId, buffer: buffer, boxed: false) - serializeInt32(readMaxId, buffer: buffer, boxed: false) - break - case .updateReadChannelInbox(let flags, let folderId, let channelId, let maxId, let stillUnreadCount, let pts): - if boxed { - buffer.appendInt32(-1842450928) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} - serializeInt64(channelId, buffer: buffer, boxed: false) - serializeInt32(maxId, buffer: buffer, boxed: false) - serializeInt32(stillUnreadCount, buffer: buffer, boxed: false) - serializeInt32(pts, buffer: buffer, boxed: false) - break - case .updateReadChannelOutbox(let channelId, let maxId): - if boxed { - buffer.appendInt32(-1218471511) - } - serializeInt64(channelId, buffer: buffer, boxed: false) - serializeInt32(maxId, buffer: buffer, boxed: false) - break - case .updateReadFeaturedEmojiStickers: - if boxed { - buffer.appendInt32(-78886548) - } - - break - case .updateReadFeaturedStickers: - if boxed { - buffer.appendInt32(1461528386) - } - - break - case .updateReadHistoryInbox(let flags, let folderId, let peer, let maxId, let stillUnreadCount, let pts, let ptsCount): - if boxed { - buffer.appendInt32(-1667805217) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} - peer.serialize(buffer, true) - serializeInt32(maxId, buffer: buffer, boxed: false) - serializeInt32(stillUnreadCount, buffer: buffer, boxed: false) - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - break - case .updateReadHistoryOutbox(let peer, let maxId, let pts, let ptsCount): - if boxed { - buffer.appendInt32(791617983) - } - peer.serialize(buffer, true) - serializeInt32(maxId, buffer: buffer, boxed: false) - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - break - case .updateReadMessagesContents(let flags, let messages, let pts, let ptsCount, let date): - if boxed { - buffer.appendInt32(-131960447) - } - serializeInt32(flags, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - serializeInt32(item, buffer: buffer, boxed: false) - } - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(date!, buffer: buffer, boxed: false)} - break - case .updateReadStories(let peer, let maxId): - if boxed { - buffer.appendInt32(-145845461) - } - peer.serialize(buffer, true) - serializeInt32(maxId, buffer: buffer, boxed: false) - break - case .updateRecentEmojiStatuses: - if boxed { - buffer.appendInt32(821314523) - } - - break - case .updateRecentReactions: - if boxed { - buffer.appendInt32(1870160884) - } - - break - case .updateRecentStickers: - if boxed { - buffer.appendInt32(-1706939360) - } - - break - case .updateSavedGifs: - if boxed { - buffer.appendInt32(-1821035490) - } - - break - case .updateSavedRingtones: - if boxed { - buffer.appendInt32(1960361625) - } - - break - case .updateSentStoryReaction(let peer, let storyId, let reaction): - if boxed { - buffer.appendInt32(2103604867) - } - peer.serialize(buffer, true) - serializeInt32(storyId, buffer: buffer, boxed: false) - reaction.serialize(buffer, true) - break - case .updateServiceNotification(let flags, let inboxDate, let type, let message, let media, let entities): - if boxed { - buffer.appendInt32(-337352679) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(inboxDate!, buffer: buffer, boxed: false)} - serializeString(type, buffer: buffer, boxed: false) - serializeString(message, buffer: buffer, boxed: false) - media.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities.count)) - for item in entities { - item.serialize(buffer, true) - } - break - case .updateStickerSets(let flags): - if boxed { - buffer.appendInt32(834816008) - } - serializeInt32(flags, buffer: buffer, boxed: false) - break - case .updateStickerSetsOrder(let flags, let order): - if boxed { - buffer.appendInt32(196268545) - } - serializeInt32(flags, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(order.count)) - for item in order { - serializeInt64(item, buffer: buffer, boxed: false) - } - break - case .updateStoriesStealthMode(let stealthMode): - if boxed { - buffer.appendInt32(738741697) - } - stealthMode.serialize(buffer, true) - break - case .updateStory(let peer, let story): - if boxed { - buffer.appendInt32(1974712216) - } - peer.serialize(buffer, true) - story.serialize(buffer, true) - break - case .updateStoryID(let id, let randomId): - if boxed { - buffer.appendInt32(468923833) - } - serializeInt32(id, buffer: buffer, boxed: false) - serializeInt64(randomId, buffer: buffer, boxed: false) - break - case .updateTheme(let theme): - if boxed { - buffer.appendInt32(-2112423005) - } - theme.serialize(buffer, true) - break - case .updateTranscribedAudio(let flags, let peer, let msgId, let transcriptionId, let text): - if boxed { - buffer.appendInt32(8703322) - } - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - serializeInt64(transcriptionId, buffer: buffer, boxed: false) - serializeString(text, buffer: buffer, boxed: false) - break - case .updateUser(let userId): - if boxed { - buffer.appendInt32(542282808) - } - serializeInt64(userId, buffer: buffer, boxed: false) - break - case .updateUserEmojiStatus(let userId, let emojiStatus): - if boxed { - buffer.appendInt32(674706841) - } - serializeInt64(userId, buffer: buffer, boxed: false) - emojiStatus.serialize(buffer, true) - break - case .updateUserName(let userId, let firstName, let lastName, let usernames): - if boxed { - buffer.appendInt32(-1484486364) - } - serializeInt64(userId, buffer: buffer, boxed: false) - serializeString(firstName, buffer: buffer, boxed: false) - serializeString(lastName, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(usernames.count)) - for item in usernames { - item.serialize(buffer, true) - } - break - case .updateUserPhone(let userId, let phone): - if boxed { - buffer.appendInt32(88680979) - } - serializeInt64(userId, buffer: buffer, boxed: false) - serializeString(phone, buffer: buffer, boxed: false) - break - case .updateUserStatus(let userId, let status): - if boxed { - buffer.appendInt32(-440534818) - } - serializeInt64(userId, buffer: buffer, boxed: false) - status.serialize(buffer, true) - break - case .updateUserTyping(let userId, let action): - if boxed { - buffer.appendInt32(-1071741569) - } - serializeInt64(userId, buffer: buffer, boxed: false) - action.serialize(buffer, true) - break - case .updateWebPage(let webpage, let pts, let ptsCount): - if boxed { - buffer.appendInt32(2139689491) - } - webpage.serialize(buffer, true) - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - break - case .updateWebViewResultSent(let queryId): - if boxed { - buffer.appendInt32(361936797) - } - serializeInt64(queryId, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .updateAttachMenuBots: - return ("updateAttachMenuBots", []) - case .updateAutoSaveSettings: - return ("updateAutoSaveSettings", []) - case .updateBotCallbackQuery(let flags, let queryId, let userId, let peer, let msgId, let chatInstance, let data, let gameShortName): - return ("updateBotCallbackQuery", [("flags", flags as Any), ("queryId", queryId as Any), ("userId", userId as Any), ("peer", peer as Any), ("msgId", msgId as Any), ("chatInstance", chatInstance as Any), ("data", data as Any), ("gameShortName", gameShortName as Any)]) - case .updateBotChatInviteRequester(let peer, let date, let userId, let about, let invite, let qts): - return ("updateBotChatInviteRequester", [("peer", peer as Any), ("date", date as Any), ("userId", userId as Any), ("about", about as Any), ("invite", invite as Any), ("qts", qts as Any)]) - case .updateBotCommands(let peer, let botId, let commands): - return ("updateBotCommands", [("peer", peer as Any), ("botId", botId as Any), ("commands", commands as Any)]) - case .updateBotInlineQuery(let flags, let queryId, let userId, let query, let geo, let peerType, let offset): - return ("updateBotInlineQuery", [("flags", flags as Any), ("queryId", queryId as Any), ("userId", userId as Any), ("query", query as Any), ("geo", geo as Any), ("peerType", peerType as Any), ("offset", offset as Any)]) - case .updateBotInlineSend(let flags, let userId, let query, let geo, let id, let msgId): - return ("updateBotInlineSend", [("flags", flags as Any), ("userId", userId as Any), ("query", query as Any), ("geo", geo as Any), ("id", id as Any), ("msgId", msgId as Any)]) - case .updateBotMenuButton(let botId, let button): - return ("updateBotMenuButton", [("botId", botId as Any), ("button", button as Any)]) - case .updateBotPrecheckoutQuery(let flags, let queryId, let userId, let payload, let info, let shippingOptionId, let currency, let totalAmount): - return ("updateBotPrecheckoutQuery", [("flags", flags as Any), ("queryId", queryId as Any), ("userId", userId as Any), ("payload", payload as Any), ("info", info as Any), ("shippingOptionId", shippingOptionId as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any)]) - case .updateBotShippingQuery(let queryId, let userId, let payload, let shippingAddress): - return ("updateBotShippingQuery", [("queryId", queryId as Any), ("userId", userId as Any), ("payload", payload as Any), ("shippingAddress", shippingAddress as Any)]) - case .updateBotStopped(let userId, let date, let stopped, let qts): - return ("updateBotStopped", [("userId", userId as Any), ("date", date as Any), ("stopped", stopped as Any), ("qts", qts as Any)]) - case .updateBotWebhookJSON(let data): - return ("updateBotWebhookJSON", [("data", data as Any)]) - case .updateBotWebhookJSONQuery(let queryId, let data, let timeout): - return ("updateBotWebhookJSONQuery", [("queryId", queryId as Any), ("data", data as Any), ("timeout", timeout as Any)]) - case .updateChannel(let channelId): - return ("updateChannel", [("channelId", channelId as Any)]) - case .updateChannelAvailableMessages(let channelId, let availableMinId): - return ("updateChannelAvailableMessages", [("channelId", channelId as Any), ("availableMinId", availableMinId as Any)]) - case .updateChannelMessageForwards(let channelId, let id, let forwards): - return ("updateChannelMessageForwards", [("channelId", channelId as Any), ("id", id as Any), ("forwards", forwards as Any)]) - case .updateChannelMessageViews(let channelId, let id, let views): - return ("updateChannelMessageViews", [("channelId", channelId as Any), ("id", id as Any), ("views", views as Any)]) - case .updateChannelParticipant(let flags, let channelId, let date, let actorId, let userId, let prevParticipant, let newParticipant, let invite, let qts): - return ("updateChannelParticipant", [("flags", flags as Any), ("channelId", channelId as Any), ("date", date as Any), ("actorId", actorId as Any), ("userId", userId as Any), ("prevParticipant", prevParticipant as Any), ("newParticipant", newParticipant as Any), ("invite", invite as Any), ("qts", qts as Any)]) - case .updateChannelPinnedTopic(let flags, let channelId, let topicId): - return ("updateChannelPinnedTopic", [("flags", flags as Any), ("channelId", channelId as Any), ("topicId", topicId as Any)]) - case .updateChannelPinnedTopics(let flags, let channelId, let order): - return ("updateChannelPinnedTopics", [("flags", flags as Any), ("channelId", channelId as Any), ("order", order as Any)]) - case .updateChannelReadMessagesContents(let flags, let channelId, let topMsgId, let messages): - return ("updateChannelReadMessagesContents", [("flags", flags as Any), ("channelId", channelId as Any), ("topMsgId", topMsgId as Any), ("messages", messages as Any)]) - case .updateChannelTooLong(let flags, let channelId, let pts): - return ("updateChannelTooLong", [("flags", flags as Any), ("channelId", channelId as Any), ("pts", pts as Any)]) - case .updateChannelUserTyping(let flags, let channelId, let topMsgId, let fromId, let action): - return ("updateChannelUserTyping", [("flags", flags as Any), ("channelId", channelId as Any), ("topMsgId", topMsgId as Any), ("fromId", fromId as Any), ("action", action as Any)]) - case .updateChannelWebPage(let channelId, let webpage, let pts, let ptsCount): - return ("updateChannelWebPage", [("channelId", channelId as Any), ("webpage", webpage as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) - case .updateChat(let chatId): - return ("updateChat", [("chatId", chatId as Any)]) - case .updateChatDefaultBannedRights(let peer, let defaultBannedRights, let version): - return ("updateChatDefaultBannedRights", [("peer", peer as Any), ("defaultBannedRights", defaultBannedRights as Any), ("version", version as Any)]) - case .updateChatParticipant(let flags, let chatId, let date, let actorId, let userId, let prevParticipant, let newParticipant, let invite, let qts): - return ("updateChatParticipant", [("flags", flags as Any), ("chatId", chatId as Any), ("date", date as Any), ("actorId", actorId as Any), ("userId", userId as Any), ("prevParticipant", prevParticipant as Any), ("newParticipant", newParticipant as Any), ("invite", invite as Any), ("qts", qts as Any)]) - case .updateChatParticipantAdd(let chatId, let userId, let inviterId, let date, let version): - return ("updateChatParticipantAdd", [("chatId", chatId as Any), ("userId", userId as Any), ("inviterId", inviterId as Any), ("date", date as Any), ("version", version as Any)]) - case .updateChatParticipantAdmin(let chatId, let userId, let isAdmin, let version): - return ("updateChatParticipantAdmin", [("chatId", chatId as Any), ("userId", userId as Any), ("isAdmin", isAdmin as Any), ("version", version as Any)]) - case .updateChatParticipantDelete(let chatId, let userId, let version): - return ("updateChatParticipantDelete", [("chatId", chatId as Any), ("userId", userId as Any), ("version", version as Any)]) - case .updateChatParticipants(let participants): - return ("updateChatParticipants", [("participants", participants as Any)]) - case .updateChatUserTyping(let chatId, let fromId, let action): - return ("updateChatUserTyping", [("chatId", chatId as Any), ("fromId", fromId as Any), ("action", action as Any)]) - case .updateConfig: - return ("updateConfig", []) - case .updateContactsReset: - return ("updateContactsReset", []) - case .updateDcOptions(let dcOptions): - return ("updateDcOptions", [("dcOptions", dcOptions as Any)]) - case .updateDeleteChannelMessages(let channelId, let messages, let pts, let ptsCount): - return ("updateDeleteChannelMessages", [("channelId", channelId as Any), ("messages", messages as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) - case .updateDeleteMessages(let messages, let pts, let ptsCount): - return ("updateDeleteMessages", [("messages", messages as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) - case .updateDeleteScheduledMessages(let peer, let messages): - return ("updateDeleteScheduledMessages", [("peer", peer as Any), ("messages", messages as Any)]) - case .updateDialogFilter(let flags, let id, let filter): - return ("updateDialogFilter", [("flags", flags as Any), ("id", id as Any), ("filter", filter as Any)]) - case .updateDialogFilterOrder(let order): - return ("updateDialogFilterOrder", [("order", order as Any)]) - case .updateDialogFilters: - return ("updateDialogFilters", []) - case .updateDialogPinned(let flags, let folderId, let peer): - return ("updateDialogPinned", [("flags", flags as Any), ("folderId", folderId as Any), ("peer", peer as Any)]) - case .updateDialogUnreadMark(let flags, let peer): - return ("updateDialogUnreadMark", [("flags", flags as Any), ("peer", peer as Any)]) - case .updateDraftMessage(let flags, let peer, let topMsgId, let draft): - return ("updateDraftMessage", [("flags", flags as Any), ("peer", peer as Any), ("topMsgId", topMsgId as Any), ("draft", draft as Any)]) - case .updateEditChannelMessage(let message, let pts, let ptsCount): - return ("updateEditChannelMessage", [("message", message as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) - case .updateEditMessage(let message, let pts, let ptsCount): - return ("updateEditMessage", [("message", message as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) - case .updateEncryptedChatTyping(let chatId): - return ("updateEncryptedChatTyping", [("chatId", chatId as Any)]) - case .updateEncryptedMessagesRead(let chatId, let maxDate, let date): - return ("updateEncryptedMessagesRead", [("chatId", chatId as Any), ("maxDate", maxDate as Any), ("date", date as Any)]) - case .updateEncryption(let chat, let date): - return ("updateEncryption", [("chat", chat as Any), ("date", date as Any)]) - case .updateFavedStickers: - return ("updateFavedStickers", []) - case .updateFolderPeers(let folderPeers, let pts, let ptsCount): - return ("updateFolderPeers", [("folderPeers", folderPeers as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) - case .updateGeoLiveViewed(let peer, let msgId): - return ("updateGeoLiveViewed", [("peer", peer as Any), ("msgId", msgId as Any)]) - case .updateGroupCall(let chatId, let call): - return ("updateGroupCall", [("chatId", chatId as Any), ("call", call as Any)]) - case .updateGroupCallConnection(let flags, let params): - return ("updateGroupCallConnection", [("flags", flags as Any), ("params", params as Any)]) - case .updateGroupCallParticipants(let call, let participants, let version): - return ("updateGroupCallParticipants", [("call", call as Any), ("participants", participants as Any), ("version", version as Any)]) - case .updateGroupInvitePrivacyForbidden(let userId): - return ("updateGroupInvitePrivacyForbidden", [("userId", userId as Any)]) - case .updateInlineBotCallbackQuery(let flags, let queryId, let userId, let msgId, let chatInstance, let data, let gameShortName): - return ("updateInlineBotCallbackQuery", [("flags", flags as Any), ("queryId", queryId as Any), ("userId", userId as Any), ("msgId", msgId as Any), ("chatInstance", chatInstance as Any), ("data", data as Any), ("gameShortName", gameShortName as Any)]) - case .updateLangPack(let difference): - return ("updateLangPack", [("difference", difference as Any)]) - case .updateLangPackTooLong(let langCode): - return ("updateLangPackTooLong", [("langCode", langCode as Any)]) - case .updateLoginToken: - return ("updateLoginToken", []) - case .updateMessageExtendedMedia(let peer, let msgId, let extendedMedia): - return ("updateMessageExtendedMedia", [("peer", peer as Any), ("msgId", msgId as Any), ("extendedMedia", extendedMedia as Any)]) - case .updateMessageID(let id, let randomId): - return ("updateMessageID", [("id", id as Any), ("randomId", randomId as Any)]) - case .updateMessagePoll(let flags, let pollId, let poll, let results): - return ("updateMessagePoll", [("flags", flags as Any), ("pollId", pollId as Any), ("poll", poll as Any), ("results", results as Any)]) - case .updateMessagePollVote(let pollId, let peer, let options, let qts): - return ("updateMessagePollVote", [("pollId", pollId as Any), ("peer", peer as Any), ("options", options as Any), ("qts", qts as Any)]) - case .updateMessageReactions(let flags, let peer, let msgId, let topMsgId, let reactions): - return ("updateMessageReactions", [("flags", flags as Any), ("peer", peer as Any), ("msgId", msgId as Any), ("topMsgId", topMsgId as Any), ("reactions", reactions as Any)]) - case .updateMoveStickerSetToTop(let flags, let stickerset): - return ("updateMoveStickerSetToTop", [("flags", flags as Any), ("stickerset", stickerset as Any)]) - case .updateNewAuthorization(let flags, let hash, let date, let device, let location): - return ("updateNewAuthorization", [("flags", flags as Any), ("hash", hash as Any), ("date", date as Any), ("device", device as Any), ("location", location as Any)]) - case .updateNewChannelMessage(let message, let pts, let ptsCount): - return ("updateNewChannelMessage", [("message", message as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) - case .updateNewEncryptedMessage(let message, let qts): - return ("updateNewEncryptedMessage", [("message", message as Any), ("qts", qts as Any)]) - case .updateNewMessage(let message, let pts, let ptsCount): - return ("updateNewMessage", [("message", message as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) - case .updateNewScheduledMessage(let message): - return ("updateNewScheduledMessage", [("message", message as Any)]) - case .updateNewStickerSet(let stickerset): - return ("updateNewStickerSet", [("stickerset", stickerset as Any)]) - case .updateNotifySettings(let peer, let notifySettings): - return ("updateNotifySettings", [("peer", peer as Any), ("notifySettings", notifySettings as Any)]) - case .updatePeerBlocked(let flags, let peerId): - return ("updatePeerBlocked", [("flags", flags as Any), ("peerId", peerId as Any)]) - case .updatePeerHistoryTTL(let flags, let peer, let ttlPeriod): - return ("updatePeerHistoryTTL", [("flags", flags as Any), ("peer", peer as Any), ("ttlPeriod", ttlPeriod as Any)]) - case .updatePeerLocated(let peers): - return ("updatePeerLocated", [("peers", peers as Any)]) - case .updatePeerSettings(let peer, let settings): - return ("updatePeerSettings", [("peer", peer as Any), ("settings", settings as Any)]) - case .updatePendingJoinRequests(let peer, let requestsPending, let recentRequesters): - return ("updatePendingJoinRequests", [("peer", peer as Any), ("requestsPending", requestsPending as Any), ("recentRequesters", recentRequesters as Any)]) - case .updatePhoneCall(let phoneCall): - return ("updatePhoneCall", [("phoneCall", phoneCall as Any)]) - case .updatePhoneCallSignalingData(let phoneCallId, let data): - return ("updatePhoneCallSignalingData", [("phoneCallId", phoneCallId as Any), ("data", data as Any)]) - case .updatePinnedChannelMessages(let flags, let channelId, let messages, let pts, let ptsCount): - return ("updatePinnedChannelMessages", [("flags", flags as Any), ("channelId", channelId as Any), ("messages", messages as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) - case .updatePinnedDialogs(let flags, let folderId, let order): - return ("updatePinnedDialogs", [("flags", flags as Any), ("folderId", folderId as Any), ("order", order as Any)]) - case .updatePinnedMessages(let flags, let peer, let messages, let pts, let ptsCount): - return ("updatePinnedMessages", [("flags", flags as Any), ("peer", peer as Any), ("messages", messages as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) - case .updatePrivacy(let key, let rules): - return ("updatePrivacy", [("key", key as Any), ("rules", rules as Any)]) - case .updatePtsChanged: - return ("updatePtsChanged", []) - case .updateReadChannelDiscussionInbox(let flags, let channelId, let topMsgId, let readMaxId, let broadcastId, let broadcastPost): - return ("updateReadChannelDiscussionInbox", [("flags", flags as Any), ("channelId", channelId as Any), ("topMsgId", topMsgId as Any), ("readMaxId", readMaxId as Any), ("broadcastId", broadcastId as Any), ("broadcastPost", broadcastPost as Any)]) - case .updateReadChannelDiscussionOutbox(let channelId, let topMsgId, let readMaxId): - return ("updateReadChannelDiscussionOutbox", [("channelId", channelId as Any), ("topMsgId", topMsgId as Any), ("readMaxId", readMaxId as Any)]) - case .updateReadChannelInbox(let flags, let folderId, let channelId, let maxId, let stillUnreadCount, let pts): - return ("updateReadChannelInbox", [("flags", flags as Any), ("folderId", folderId as Any), ("channelId", channelId as Any), ("maxId", maxId as Any), ("stillUnreadCount", stillUnreadCount as Any), ("pts", pts as Any)]) - case .updateReadChannelOutbox(let channelId, let maxId): - return ("updateReadChannelOutbox", [("channelId", channelId as Any), ("maxId", maxId as Any)]) - case .updateReadFeaturedEmojiStickers: - return ("updateReadFeaturedEmojiStickers", []) - case .updateReadFeaturedStickers: - return ("updateReadFeaturedStickers", []) - case .updateReadHistoryInbox(let flags, let folderId, let peer, let maxId, let stillUnreadCount, let pts, let ptsCount): - return ("updateReadHistoryInbox", [("flags", flags as Any), ("folderId", folderId as Any), ("peer", peer as Any), ("maxId", maxId as Any), ("stillUnreadCount", stillUnreadCount as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) - case .updateReadHistoryOutbox(let peer, let maxId, let pts, let ptsCount): - return ("updateReadHistoryOutbox", [("peer", peer as Any), ("maxId", maxId as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) - case .updateReadMessagesContents(let flags, let messages, let pts, let ptsCount, let date): - return ("updateReadMessagesContents", [("flags", flags as Any), ("messages", messages as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any), ("date", date as Any)]) - case .updateReadStories(let peer, let maxId): - return ("updateReadStories", [("peer", peer as Any), ("maxId", maxId as Any)]) - case .updateRecentEmojiStatuses: - return ("updateRecentEmojiStatuses", []) - case .updateRecentReactions: - return ("updateRecentReactions", []) - case .updateRecentStickers: - return ("updateRecentStickers", []) - case .updateSavedGifs: - return ("updateSavedGifs", []) - case .updateSavedRingtones: - return ("updateSavedRingtones", []) - case .updateSentStoryReaction(let peer, let storyId, let reaction): - return ("updateSentStoryReaction", [("peer", peer as Any), ("storyId", storyId as Any), ("reaction", reaction as Any)]) - case .updateServiceNotification(let flags, let inboxDate, let type, let message, let media, let entities): - return ("updateServiceNotification", [("flags", flags as Any), ("inboxDate", inboxDate as Any), ("type", type as Any), ("message", message as Any), ("media", media as Any), ("entities", entities as Any)]) - case .updateStickerSets(let flags): - return ("updateStickerSets", [("flags", flags as Any)]) - case .updateStickerSetsOrder(let flags, let order): - return ("updateStickerSetsOrder", [("flags", flags as Any), ("order", order as Any)]) - case .updateStoriesStealthMode(let stealthMode): - return ("updateStoriesStealthMode", [("stealthMode", stealthMode as Any)]) - case .updateStory(let peer, let story): - return ("updateStory", [("peer", peer as Any), ("story", story as Any)]) - case .updateStoryID(let id, let randomId): - return ("updateStoryID", [("id", id as Any), ("randomId", randomId as Any)]) - case .updateTheme(let theme): - return ("updateTheme", [("theme", theme as Any)]) - case .updateTranscribedAudio(let flags, let peer, let msgId, let transcriptionId, let text): - return ("updateTranscribedAudio", [("flags", flags as Any), ("peer", peer as Any), ("msgId", msgId as Any), ("transcriptionId", transcriptionId as Any), ("text", text as Any)]) - case .updateUser(let userId): - return ("updateUser", [("userId", userId as Any)]) - case .updateUserEmojiStatus(let userId, let emojiStatus): - return ("updateUserEmojiStatus", [("userId", userId as Any), ("emojiStatus", emojiStatus as Any)]) - case .updateUserName(let userId, let firstName, let lastName, let usernames): - return ("updateUserName", [("userId", userId as Any), ("firstName", firstName as Any), ("lastName", lastName as Any), ("usernames", usernames as Any)]) - case .updateUserPhone(let userId, let phone): - return ("updateUserPhone", [("userId", userId as Any), ("phone", phone as Any)]) - case .updateUserStatus(let userId, let status): - return ("updateUserStatus", [("userId", userId as Any), ("status", status as Any)]) - case .updateUserTyping(let userId, let action): - return ("updateUserTyping", [("userId", userId as Any), ("action", action as Any)]) - case .updateWebPage(let webpage, let pts, let ptsCount): - return ("updateWebPage", [("webpage", webpage as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) - case .updateWebViewResultSent(let queryId): - return ("updateWebViewResultSent", [("queryId", queryId as Any)]) - } - } - - public static func parse_updateAttachMenuBots(_ reader: BufferReader) -> Update? { - return Api.Update.updateAttachMenuBots - } - public static func parse_updateAutoSaveSettings(_ reader: BufferReader) -> Update? { - return Api.Update.updateAutoSaveSettings - } - public static func parse_updateBotCallbackQuery(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int64? - _3 = reader.readInt64() - var _4: Api.Peer? - if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _5: Int32? - _5 = reader.readInt32() - var _6: Int64? - _6 = reader.readInt64() - var _7: Buffer? - if Int(_1!) & Int(1 << 0) != 0 {_7 = parseBytes(reader) } - var _8: String? - if Int(_1!) & Int(1 << 1) != 0 {_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 << 0) == 0) || _7 != nil - let _c8 = (Int(_1!) & Int(1 << 1) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.Update.updateBotCallbackQuery(flags: _1!, queryId: _2!, userId: _3!, peer: _4!, msgId: _5!, chatInstance: _6!, data: _7, gameShortName: _8) - } - else { - return nil - } - } - public static func parse_updateBotChatInviteRequester(_ reader: BufferReader) -> Update? { - var _1: Api.Peer? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _2: Int32? - _2 = reader.readInt32() - var _3: Int64? - _3 = reader.readInt64() - var _4: String? - _4 = parseString(reader) - var _5: Api.ExportedChatInvite? - if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite - } - var _6: Int32? - _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 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.Update.updateBotChatInviteRequester(peer: _1!, date: _2!, userId: _3!, about: _4!, invite: _5!, qts: _6!) - } - else { - return nil - } - } - public static func parse_updateBotCommands(_ reader: BufferReader) -> Update? { - var _1: Api.Peer? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _2: Int64? - _2 = reader.readInt64() - var _3: [Api.BotCommand]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.BotCommand.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateBotCommands(peer: _1!, botId: _2!, commands: _3!) - } - else { - return nil - } - } - public static func parse_updateBotInlineQuery(_ reader: BufferReader) -> Update? { - 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: Api.GeoPoint? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.GeoPoint - } } - var _6: Api.InlineQueryPeerType? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.InlineQueryPeerType - } } - var _7: String? - _7 = parseString(reader) - 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 << 1) == 0) || _6 != nil - let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.Update.updateBotInlineQuery(flags: _1!, queryId: _2!, userId: _3!, query: _4!, geo: _5, peerType: _6, offset: _7!) - } - else { - return nil - } - } - public static func parse_updateBotInlineSend(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: String? - _3 = parseString(reader) - var _4: Api.GeoPoint? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.GeoPoint - } } - var _5: String? - _5 = parseString(reader) - var _6: Api.InputBotInlineMessageID? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.InputBotInlineMessageID - } } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - let _c5 = _5 != nil - let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.Update.updateBotInlineSend(flags: _1!, userId: _2!, query: _3!, geo: _4, id: _5!, msgId: _6) - } - else { - return nil - } - } - public static func parse_updateBotMenuButton(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Api.BotMenuButton? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.BotMenuButton - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateBotMenuButton(botId: _1!, button: _2!) - } - else { - return nil - } - } - public static func parse_updateBotPrecheckoutQuery(_ reader: BufferReader) -> Update? { - 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: Api.PaymentRequestedInfo? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo - } } - var _6: String? - if Int(_1!) & Int(1 << 1) != 0 {_6 = parseString(reader) } - var _7: String? - _7 = parseString(reader) - var _8: Int64? - _8 = reader.readInt64() - 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 << 1) == 0) || _6 != nil - let _c7 = _7 != nil - let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.Update.updateBotPrecheckoutQuery(flags: _1!, queryId: _2!, userId: _3!, payload: _4!, info: _5, shippingOptionId: _6, currency: _7!, totalAmount: _8!) - } - else { - return nil - } - } - public static func parse_updateBotShippingQuery(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int64? - _2 = reader.readInt64() - var _3: Buffer? - _3 = parseBytes(reader) - var _4: Api.PostAddress? - if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.PostAddress - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateBotShippingQuery(queryId: _1!, userId: _2!, payload: _3!, shippingAddress: _4!) - } - else { - return nil - } - } - public static func parse_updateBotStopped(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int32? - _2 = reader.readInt32() - var _3: Api.Bool? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.Bool - } - 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.Update.updateBotStopped(userId: _1!, date: _2!, stopped: _3!, qts: _4!) - } - else { - return nil - } - } - public static func parse_updateBotWebhookJSON(_ reader: BufferReader) -> Update? { - var _1: Api.DataJSON? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.DataJSON - } - let _c1 = _1 != nil - if _c1 { - return Api.Update.updateBotWebhookJSON(data: _1!) - } - else { - return nil - } - } - public static func parse_updateBotWebhookJSONQuery(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Api.DataJSON? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.DataJSON - } - var _3: Int32? - _3 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateBotWebhookJSONQuery(queryId: _1!, data: _2!, timeout: _3!) - } - else { - return nil - } - } - public static func parse_updateChannel(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - let _c1 = _1 != nil - if _c1 { - return Api.Update.updateChannel(channelId: _1!) - } - else { - return nil - } - } - public static func parse_updateChannelAvailableMessages(_ reader: BufferReader) -> Update? { - 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.Update.updateChannelAvailableMessages(channelId: _1!, availableMinId: _2!) - } - else { - return nil - } - } - public static func parse_updateChannelMessageForwards(_ reader: BufferReader) -> Update? { - 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.Update.updateChannelMessageForwards(channelId: _1!, id: _2!, forwards: _3!) - } - else { - return nil - } - } - public static func parse_updateChannelMessageViews(_ reader: BufferReader) -> Update? { - 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.Update.updateChannelMessageViews(channelId: _1!, id: _2!, views: _3!) - } - else { - return nil - } - } - public static func parse_updateChannelParticipant(_ reader: BufferReader) -> Update? { - 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: Api.ChannelParticipant? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.ChannelParticipant - } } - var _7: Api.ChannelParticipant? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.ChannelParticipant - } } - var _8: Api.ExportedChatInvite? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite - } } - var _9: Int32? - _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 << 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 - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.Update.updateChannelParticipant(flags: _1!, channelId: _2!, date: _3!, actorId: _4!, userId: _5!, prevParticipant: _6, newParticipant: _7, invite: _8, qts: _9!) - } - else { - return nil - } - } - public static func parse_updateChannelPinnedTopic(_ reader: BufferReader) -> Update? { - 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.Update.updateChannelPinnedTopic(flags: _1!, channelId: _2!, topicId: _3!) - } - else { - return nil - } - } - public static func parse_updateChannelPinnedTopics(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: [Int32]? - if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateChannelPinnedTopics(flags: _1!, channelId: _2!, order: _3) - } - else { - return nil - } - } - public static func parse_updateChannelReadMessagesContents(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } - var _4: [Int32]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateChannelReadMessagesContents(flags: _1!, channelId: _2!, topMsgId: _3, messages: _4!) - } - else { - return nil - } - } - public static func parse_updateChannelTooLong(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateChannelTooLong(flags: _1!, channelId: _2!, pts: _3) - } - else { - return nil - } - } - public static func parse_updateChannelUserTyping(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } - var _4: Api.Peer? - if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _5: Api.SendMessageAction? - if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.SendMessageAction - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Update.updateChannelUserTyping(flags: _1!, channelId: _2!, topMsgId: _3, fromId: _4!, action: _5!) - } - else { - return nil - } - } - public static func parse_updateChannelWebPage(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Api.WebPage? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.WebPage - } - 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.Update.updateChannelWebPage(channelId: _1!, webpage: _2!, pts: _3!, ptsCount: _4!) - } - else { - return nil - } - } - public static func parse_updateChat(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - let _c1 = _1 != nil - if _c1 { - return Api.Update.updateChat(chatId: _1!) - } - else { - return nil - } - } - public static func parse_updateChatDefaultBannedRights(_ reader: BufferReader) -> Update? { - var _1: Api.Peer? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _2: Api.ChatBannedRights? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.ChatBannedRights - } - var _3: Int32? - _3 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateChatDefaultBannedRights(peer: _1!, defaultBannedRights: _2!, version: _3!) - } - else { - return nil - } - } - public static func parse_updateChatParticipant(_ reader: BufferReader) -> Update? { - 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: Api.ChatParticipant? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.ChatParticipant - } } - var _7: Api.ChatParticipant? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.ChatParticipant - } } - var _8: Api.ExportedChatInvite? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite - } } - var _9: Int32? - _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 << 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 - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.Update.updateChatParticipant(flags: _1!, chatId: _2!, date: _3!, actorId: _4!, userId: _5!, prevParticipant: _6, newParticipant: _7, invite: _8, qts: _9!) - } - else { - return nil - } - } - public static func parse_updateChatParticipantAdd(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int64? - _3 = reader.readInt64() - var _4: Int32? - _4 = reader.readInt32() - var _5: Int32? - _5 = reader.readInt32() - 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.Update.updateChatParticipantAdd(chatId: _1!, userId: _2!, inviterId: _3!, date: _4!, version: _5!) - } - else { - return nil - } - } - public static func parse_updateChatParticipantAdmin(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int64? - _2 = reader.readInt64() - var _3: Api.Bool? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.Bool - } - 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.Update.updateChatParticipantAdmin(chatId: _1!, userId: _2!, isAdmin: _3!, version: _4!) - } - else { - return nil - } - } - public static func parse_updateChatParticipantDelete(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - 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.Update.updateChatParticipantDelete(chatId: _1!, userId: _2!, version: _3!) - } - else { - return nil - } - } - public static func parse_updateChatParticipants(_ reader: BufferReader) -> Update? { - var _1: Api.ChatParticipants? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.ChatParticipants - } - let _c1 = _1 != nil - if _c1 { - return Api.Update.updateChatParticipants(participants: _1!) - } - else { - return nil - } - } - public static func parse_updateChatUserTyping(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Api.Peer? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _3: Api.SendMessageAction? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.SendMessageAction - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateChatUserTyping(chatId: _1!, fromId: _2!, action: _3!) - } - else { - return nil - } - } - public static func parse_updateConfig(_ reader: BufferReader) -> Update? { - return Api.Update.updateConfig - } - public static func parse_updateContactsReset(_ reader: BufferReader) -> Update? { - return Api.Update.updateContactsReset - } - public static func parse_updateDcOptions(_ reader: BufferReader) -> Update? { - var _1: [Api.DcOption]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DcOption.self) - } - let _c1 = _1 != nil - if _c1 { - return Api.Update.updateDcOptions(dcOptions: _1!) - } - else { - return nil - } - } - public static func parse_updateDeleteChannelMessages(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Int32]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - 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.Update.updateDeleteChannelMessages(channelId: _1!, messages: _2!, pts: _3!, ptsCount: _4!) - } - else { - return nil - } - } - public static func parse_updateDeleteMessages(_ reader: BufferReader) -> Update? { - var _1: [Int32]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - 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.Update.updateDeleteMessages(messages: _1!, pts: _2!, ptsCount: _3!) - } - else { - return nil - } - } - public static func parse_updateDeleteScheduledMessages(_ reader: BufferReader) -> Update? { - var _1: Api.Peer? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Peer - } - 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.Update.updateDeleteScheduledMessages(peer: _1!, messages: _2!) - } - else { - return nil - } - } - public static func parse_updateDialogFilter(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Api.DialogFilter? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.DialogFilter - } } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateDialogFilter(flags: _1!, id: _2!, filter: _3) - } - else { - return nil - } - } - public static func parse_updateDialogFilterOrder(_ reader: BufferReader) -> Update? { - var _1: [Int32]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - let _c1 = _1 != nil - if _c1 { - return Api.Update.updateDialogFilterOrder(order: _1!) - } - else { - return nil - } - } - public static func parse_updateDialogFilters(_ reader: BufferReader) -> Update? { - return Api.Update.updateDialogFilters - } - public static func parse_updateDialogPinned(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() } - var _3: Api.DialogPeer? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.DialogPeer - } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateDialogPinned(flags: _1!, folderId: _2, peer: _3!) - } - else { - return nil - } - } - public static func parse_updateDialogUnreadMark(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.DialogPeer? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.DialogPeer - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateDialogUnreadMark(flags: _1!, peer: _2!) - } - else { - return nil - } - } - public static func parse_updateDraftMessage(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.Peer? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _3: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } - var _4: Api.DraftMessage? - if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.DraftMessage - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateDraftMessage(flags: _1!, peer: _2!, topMsgId: _3, draft: _4!) - } - else { - return nil - } - } - public static func parse_updateEditChannelMessage(_ reader: BufferReader) -> Update? { - var _1: Api.Message? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Message - } - 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.Update.updateEditChannelMessage(message: _1!, pts: _2!, ptsCount: _3!) - } - else { - return nil - } - } - public static func parse_updateEditMessage(_ reader: BufferReader) -> Update? { - var _1: Api.Message? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Message - } - 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.Update.updateEditMessage(message: _1!, pts: _2!, ptsCount: _3!) - } - else { - return nil - } - } - public static func parse_updateEncryptedChatTyping(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.Update.updateEncryptedChatTyping(chatId: _1!) - } - else { - return nil - } - } - public static func parse_updateEncryptedMessagesRead(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - 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.Update.updateEncryptedMessagesRead(chatId: _1!, maxDate: _2!, date: _3!) - } - else { - return nil - } - } - public static func parse_updateEncryption(_ reader: BufferReader) -> Update? { - var _1: Api.EncryptedChat? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.EncryptedChat - } - var _2: Int32? - _2 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateEncryption(chat: _1!, date: _2!) - } - else { - return nil - } - } - public static func parse_updateFavedStickers(_ reader: BufferReader) -> Update? { - return Api.Update.updateFavedStickers - } - public static func parse_updateFolderPeers(_ reader: BufferReader) -> Update? { - var _1: [Api.FolderPeer]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.FolderPeer.self) - } - 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.Update.updateFolderPeers(folderPeers: _1!, pts: _2!, ptsCount: _3!) - } - else { - return nil - } - } - public static func parse_updateGeoLiveViewed(_ reader: BufferReader) -> Update? { - var _1: Api.Peer? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _2: Int32? - _2 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateGeoLiveViewed(peer: _1!, msgId: _2!) - } - else { - return nil - } - } - public static func parse_updateGroupCall(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Api.GroupCall? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.GroupCall - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateGroupCall(chatId: _1!, call: _2!) - } - else { - return nil - } - } - public static func parse_updateGroupCallConnection(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.DataJSON? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.DataJSON - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateGroupCallConnection(flags: _1!, params: _2!) - } - else { - return nil - } - } - public static func parse_updateGroupCallParticipants(_ reader: BufferReader) -> Update? { - var _1: Api.InputGroupCall? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.InputGroupCall - } - var _2: [Api.GroupCallParticipant]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.GroupCallParticipant.self) - } - var _3: Int32? - _3 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateGroupCallParticipants(call: _1!, participants: _2!, version: _3!) - } - else { - return nil - } - } - public static func parse_updateGroupInvitePrivacyForbidden(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - let _c1 = _1 != nil - if _c1 { - return Api.Update.updateGroupInvitePrivacyForbidden(userId: _1!) - } - else { - return nil - } - } - public static func parse_updateInlineBotCallbackQuery(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int64? - _3 = reader.readInt64() - var _4: Api.InputBotInlineMessageID? - if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.InputBotInlineMessageID - } - var _5: Int64? - _5 = reader.readInt64() - var _6: Buffer? - if Int(_1!) & Int(1 << 0) != 0 {_6 = parseBytes(reader) } - var _7: String? - if Int(_1!) & Int(1 << 1) != 0 {_7 = parseString(reader) } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.Update.updateInlineBotCallbackQuery(flags: _1!, queryId: _2!, userId: _3!, msgId: _4!, chatInstance: _5!, data: _6, gameShortName: _7) - } - else { - return nil - } - } - public static func parse_updateLangPack(_ reader: BufferReader) -> Update? { - var _1: Api.LangPackDifference? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.LangPackDifference - } - let _c1 = _1 != nil - if _c1 { - return Api.Update.updateLangPack(difference: _1!) - } - else { - return nil - } - } - public static func parse_updateLangPackTooLong(_ reader: BufferReader) -> Update? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.Update.updateLangPackTooLong(langCode: _1!) - } - else { - return nil - } - } - public static func parse_updateLoginToken(_ reader: BufferReader) -> Update? { - return Api.Update.updateLoginToken - } - public static func parse_updateMessageExtendedMedia(_ reader: BufferReader) -> Update? { - var _1: Api.Peer? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _2: Int32? - _2 = reader.readInt32() - var _3: Api.MessageExtendedMedia? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.MessageExtendedMedia - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateMessageExtendedMedia(peer: _1!, msgId: _2!, extendedMedia: _3!) - } - else { - return nil - } - } - public static func parse_updateMessageID(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateMessageID(id: _1!, randomId: _2!) - } - else { - return nil - } - } - public static func parse_updateMessagePoll(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Api.Poll? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.Poll - } } - var _4: Api.PollResults? - if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.PollResults - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateMessagePoll(flags: _1!, pollId: _2!, poll: _3, results: _4!) - } - else { - return nil - } - } - public static func parse_updateMessagePollVote(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Api.Peer? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _3: [Buffer]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: Buffer.self) - } - 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.Update.updateMessagePollVote(pollId: _1!, peer: _2!, options: _3!, qts: _4!) - } - else { - return nil - } - } - public static func parse_updateMessageReactions(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.Peer? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt32() } - var _5: Api.MessageReactions? - if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.MessageReactions - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Update.updateMessageReactions(flags: _1!, peer: _2!, msgId: _3!, topMsgId: _4, reactions: _5!) - } - else { - return nil - } - } - public static func parse_updateMoveStickerSetToTop(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateMoveStickerSetToTop(flags: _1!, stickerset: _2!) - } - else { - return nil - } - } - public static func parse_updateNewAuthorization(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } - var _4: String? - if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) } - var _5: String? - if Int(_1!) & Int(1 << 0) != 0 {_5 = parseString(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 << 0) == 0) || _4 != nil - let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Update.updateNewAuthorization(flags: _1!, hash: _2!, date: _3, device: _4, location: _5) - } - else { - return nil - } - } - public static func parse_updateNewChannelMessage(_ reader: BufferReader) -> Update? { - var _1: Api.Message? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Message - } - 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.Update.updateNewChannelMessage(message: _1!, pts: _2!, ptsCount: _3!) - } - else { - return nil - } - } - public static func parse_updateNewEncryptedMessage(_ reader: BufferReader) -> Update? { - var _1: Api.EncryptedMessage? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.EncryptedMessage - } - var _2: Int32? - _2 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateNewEncryptedMessage(message: _1!, qts: _2!) - } - else { - return nil - } - } - public static func parse_updateNewMessage(_ reader: BufferReader) -> Update? { - var _1: Api.Message? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Message - } - 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.Update.updateNewMessage(message: _1!, pts: _2!, ptsCount: _3!) - } - else { - return nil - } - } - public static func parse_updateNewScheduledMessage(_ reader: BufferReader) -> Update? { - var _1: Api.Message? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Message - } - let _c1 = _1 != nil - if _c1 { - return Api.Update.updateNewScheduledMessage(message: _1!) - } - else { - return nil - } - } - public static func parse_updateNewStickerSet(_ reader: BufferReader) -> Update? { - var _1: Api.messages.StickerSet? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.messages.StickerSet - } - let _c1 = _1 != nil - if _c1 { - return Api.Update.updateNewStickerSet(stickerset: _1!) - } - else { - return nil - } - } - public static func parse_updateNotifySettings(_ reader: BufferReader) -> Update? { - var _1: Api.NotifyPeer? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.NotifyPeer - } - var _2: Api.PeerNotifySettings? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateNotifySettings(peer: _1!, notifySettings: _2!) - } - else { - return nil - } - } - public static func parse_updatePeerBlocked(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.Peer? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.Peer - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updatePeerBlocked(flags: _1!, peerId: _2!) - } - else { - return nil - } - } - public static func parse_updatePeerHistoryTTL(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.Peer? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _3: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updatePeerHistoryTTL(flags: _1!, peer: _2!, ttlPeriod: _3) - } - else { - return nil - } - } - public static func parse_updatePeerLocated(_ reader: BufferReader) -> Update? { - var _1: [Api.PeerLocated]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PeerLocated.self) - } - let _c1 = _1 != nil - if _c1 { - return Api.Update.updatePeerLocated(peers: _1!) - } - else { - return nil - } - } - public static func parse_updatePeerSettings(_ reader: BufferReader) -> Update? { - var _1: Api.Peer? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _2: Api.PeerSettings? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.PeerSettings - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updatePeerSettings(peer: _1!, settings: _2!) - } - else { - return nil - } - } - public static func parse_updatePendingJoinRequests(_ reader: BufferReader) -> Update? { - var _1: Api.Peer? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _2: Int32? - _2 = reader.readInt32() - var _3: [Int64]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updatePendingJoinRequests(peer: _1!, requestsPending: _2!, recentRequesters: _3!) - } - else { - return nil - } - } - public static func parse_updatePhoneCall(_ reader: BufferReader) -> Update? { - var _1: Api.PhoneCall? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.PhoneCall - } - let _c1 = _1 != nil - if _c1 { - return Api.Update.updatePhoneCall(phoneCall: _1!) - } - else { - return nil - } - } - public static func parse_updatePhoneCallSignalingData(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Buffer? - _2 = parseBytes(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updatePhoneCallSignalingData(phoneCallId: _1!, data: _2!) - } - else { - return nil - } - } - public static func parse_updatePinnedChannelMessages(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: [Int32]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - var _4: Int32? - _4 = reader.readInt32() - var _5: Int32? - _5 = reader.readInt32() - 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.Update.updatePinnedChannelMessages(flags: _1!, channelId: _2!, messages: _3!, pts: _4!, ptsCount: _5!) - } - else { - return nil - } - } - public static func parse_updatePinnedDialogs(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() } - var _3: [Api.DialogPeer]? - if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DialogPeer.self) - } } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updatePinnedDialogs(flags: _1!, folderId: _2, order: _3) - } - else { - return nil - } - } - public static func parse_updatePinnedMessages(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.Peer? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _3: [Int32]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - var _4: Int32? - _4 = reader.readInt32() - var _5: Int32? - _5 = reader.readInt32() - 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.Update.updatePinnedMessages(flags: _1!, peer: _2!, messages: _3!, pts: _4!, ptsCount: _5!) - } - else { - return nil - } - } - public static func parse_updatePrivacy(_ reader: BufferReader) -> Update? { - var _1: Api.PrivacyKey? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.PrivacyKey - } - var _2: [Api.PrivacyRule]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PrivacyRule.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updatePrivacy(key: _1!, rules: _2!) - } - else { - return nil - } - } - public static func parse_updatePtsChanged(_ reader: BufferReader) -> Update? { - return Api.Update.updatePtsChanged - } - public static func parse_updateReadChannelDiscussionInbox(_ reader: BufferReader) -> Update? { + serializeInt32(id, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt32(expireDate, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .storyItem(let flags, let id, let date, let fwdFrom, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction): + return ("storyItem", [("flags", flags as Any), ("id", id as Any), ("date", date as Any), ("fwdFrom", fwdFrom as Any), ("expireDate", expireDate as Any), ("caption", caption as Any), ("entities", entities as Any), ("media", media as Any), ("mediaAreas", mediaAreas as Any), ("privacy", privacy as Any), ("views", views as Any), ("sentReaction", sentReaction as Any)]) + case .storyItemDeleted(let id): + return ("storyItemDeleted", [("id", id as Any)]) + case .storyItemSkipped(let flags, let id, let date, let expireDate): + return ("storyItemSkipped", [("flags", flags as Any), ("id", id as Any), ("date", date as Any), ("expireDate", expireDate as Any)]) + } + } + + public static func parse_storyItem(_ reader: BufferReader) -> StoryItem? { var _1: Int32? _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - _4 = reader.readInt32() - var _5: Int64? - if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt64() } - 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 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil - let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.Update.updateReadChannelDiscussionInbox(flags: _1!, channelId: _2!, topMsgId: _3!, readMaxId: _4!, broadcastId: _5, broadcastPost: _6) - } - else { - return nil - } - } - public static func parse_updateReadChannelDiscussionOutbox(_ reader: BufferReader) -> Update? { - 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.Update.updateReadChannelDiscussionOutbox(channelId: _1!, topMsgId: _2!, readMaxId: _3!) - } - else { - return nil - } - } - public static func parse_updateReadChannelInbox(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } - var _3: Int64? - _3 = reader.readInt64() - var _4: Int32? - _4 = reader.readInt32() + var _4: Api.StoryFwdHeader? + if Int(_1!) & Int(1 << 17) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.StoryFwdHeader + } } var _5: Int32? _5 = reader.readInt32() - var _6: Int32? - _6 = reader.readInt32() - 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 - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.Update.updateReadChannelInbox(flags: _1!, folderId: _2, channelId: _3!, maxId: _4!, stillUnreadCount: _5!, pts: _6!) - } - else { - return nil + var _6: String? + if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) } + var _7: [Api.MessageEntity]? + if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) + } } + var _8: Api.MessageMedia? + if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.MessageMedia } - } - public static func parse_updateReadChannelOutbox(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int32? - _2 = reader.readInt32() + var _9: [Api.MediaArea]? + if Int(_1!) & Int(1 << 14) != 0 {if let _ = reader.readInt32() { + _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MediaArea.self) + } } + var _10: [Api.PrivacyRule]? + if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() { + _10 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PrivacyRule.self) + } } + var _11: Api.StoryViews? + if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { + _11 = Api.parse(reader, signature: signature) as? Api.StoryViews + } } + var _12: Api.Reaction? + if Int(_1!) & Int(1 << 15) != 0 {if let signature = reader.readInt32() { + _12 = Api.parse(reader, signature: signature) as? Api.Reaction + } } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateReadChannelOutbox(channelId: _1!, maxId: _2!) + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 17) == 0) || _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 = _8 != nil + let _c9 = (Int(_1!) & Int(1 << 14) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 2) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 3) == 0) || _11 != nil + let _c12 = (Int(_1!) & Int(1 << 15) == 0) || _12 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 { + return Api.StoryItem.storyItem(flags: _1!, id: _2!, date: _3!, fwdFrom: _4, expireDate: _5!, caption: _6, entities: _7, media: _8!, mediaAreas: _9, privacy: _10, views: _11, sentReaction: _12) } else { return nil } } - public static func parse_updateReadFeaturedEmojiStickers(_ reader: BufferReader) -> Update? { - return Api.Update.updateReadFeaturedEmojiStickers - } - public static func parse_updateReadFeaturedStickers(_ reader: BufferReader) -> Update? { - return Api.Update.updateReadFeaturedStickers - } - public static func parse_updateReadHistoryInbox(_ reader: BufferReader) -> Update? { + public static func parse_storyItemDeleted(_ reader: BufferReader) -> StoryItem? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } - 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: Int32? - _5 = reader.readInt32() - var _6: Int32? - _6 = reader.readInt32() - var _7: Int32? - _7 = reader.readInt32() 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 - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.Update.updateReadHistoryInbox(flags: _1!, folderId: _2, peer: _3!, maxId: _4!, stillUnreadCount: _5!, pts: _6!, ptsCount: _7!) + if _c1 { + return Api.StoryItem.storyItemDeleted(id: _1!) } else { return nil } } - public static func parse_updateReadHistoryOutbox(_ reader: BufferReader) -> Update? { - var _1: Api.Peer? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Peer - } + public static func parse_storyItemSkipped(_ reader: BufferReader) -> StoryItem? { + var _1: Int32? + _1 = reader.readInt32() var _2: Int32? _2 = reader.readInt32() var _3: Int32? @@ -4111,349 +1181,355 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateReadHistoryOutbox(peer: _1!, maxId: _2!, pts: _3!, ptsCount: _4!) + return Api.StoryItem.storyItemSkipped(flags: _1!, id: _2!, date: _3!, expireDate: _4!) } else { return nil } } - public static func parse_updateReadMessagesContents(_ reader: BufferReader) -> Update? { + + } +} +public extension Api { + enum StoryView: TypeConstructorDescription { + case storyView(flags: Int32, userId: Int64, date: Int32, reaction: Api.Reaction?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .storyView(let flags, let userId, let date, let reaction): + if boxed { + buffer.appendInt32(-1329730875) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {reaction!.serialize(buffer, true)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .storyView(let flags, let userId, let date, let reaction): + return ("storyView", [("flags", flags as Any), ("userId", userId as Any), ("date", date as Any), ("reaction", reaction as Any)]) + } + } + + public static func parse_storyView(_ reader: BufferReader) -> StoryView? { var _1: Int32? _1 = reader.readInt32() - var _2: [Int32]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } + var _2: Int64? + _2 = reader.readInt64() var _3: Int32? _3 = reader.readInt32() - var _4: Int32? - _4 = reader.readInt32() - var _5: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_5 = 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 - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Update.updateReadMessagesContents(flags: _1!, messages: _2!, pts: _3!, ptsCount: _4!, date: _5) - } - else { - return nil - } - } - public static func parse_updateReadStories(_ reader: BufferReader) -> Update? { - var _1: Api.Peer? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _2: Int32? - _2 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateReadStories(peer: _1!, maxId: _2!) - } - else { - return nil - } - } - public static func parse_updateRecentEmojiStatuses(_ reader: BufferReader) -> Update? { - return Api.Update.updateRecentEmojiStatuses - } - public static func parse_updateRecentReactions(_ reader: BufferReader) -> Update? { - return Api.Update.updateRecentReactions - } - public static func parse_updateRecentStickers(_ reader: BufferReader) -> Update? { - return Api.Update.updateRecentStickers - } - public static func parse_updateSavedGifs(_ reader: BufferReader) -> Update? { - return Api.Update.updateSavedGifs - } - public static func parse_updateSavedRingtones(_ reader: BufferReader) -> Update? { - return Api.Update.updateSavedRingtones - } - public static func parse_updateSentStoryReaction(_ reader: BufferReader) -> Update? { - var _1: Api.Peer? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _2: Int32? - _2 = reader.readInt32() - var _3: Api.Reaction? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.Reaction - } + var _4: Api.Reaction? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.Reaction + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateSentStoryReaction(peer: _1!, storyId: _2!, reaction: _3!) + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.StoryView.storyView(flags: _1!, userId: _2!, date: _3!, reaction: _4) } else { return nil } } - public static func parse_updateServiceNotification(_ reader: BufferReader) -> Update? { + + } +} +public extension Api { + enum StoryViews: TypeConstructorDescription { + case storyViews(flags: Int32, viewsCount: Int32, forwardsCount: Int32?, reactions: [Api.ReactionCount]?, reactionsCount: Int32?, recentViewers: [Int64]?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .storyViews(let flags, let viewsCount, let forwardsCount, let reactions, let reactionsCount, let recentViewers): + if boxed { + buffer.appendInt32(-1923523370) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(viewsCount, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {serializeInt32(forwardsCount!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(reactions!.count)) + for item in reactions! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 4) != 0 {serializeInt32(reactionsCount!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(recentViewers!.count)) + for item in recentViewers! { + serializeInt64(item, buffer: buffer, boxed: false) + }} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .storyViews(let flags, let viewsCount, let forwardsCount, let reactions, let reactionsCount, let recentViewers): + return ("storyViews", [("flags", flags as Any), ("viewsCount", viewsCount as Any), ("forwardsCount", forwardsCount as Any), ("reactions", reactions as Any), ("reactionsCount", reactionsCount as Any), ("recentViewers", recentViewers as Any)]) + } + } + + public static func parse_storyViews(_ reader: BufferReader) -> StoryViews? { var _1: Int32? _1 = reader.readInt32() var _2: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() } - var _3: String? - _3 = parseString(reader) - var _4: String? - _4 = parseString(reader) - var _5: Api.MessageMedia? - if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.MessageMedia - } - var _6: [Api.MessageEntity]? - if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) - } + _2 = reader.readInt32() + var _3: Int32? + if Int(_1!) & Int(1 << 2) != 0 {_3 = reader.readInt32() } + var _4: [Api.ReactionCount]? + if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ReactionCount.self) + } } + var _5: Int32? + if Int(_1!) & Int(1 << 4) != 0 {_5 = reader.readInt32() } + var _6: [Int64]? + if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } } let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.Update.updateServiceNotification(flags: _1!, inboxDate: _2, type: _3!, message: _4!, media: _5!, entities: _6!) - } - else { - return nil - } - } - public static func parse_updateStickerSets(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.Update.updateStickerSets(flags: _1!) + return Api.StoryViews.storyViews(flags: _1!, viewsCount: _2!, forwardsCount: _3, reactions: _4, reactionsCount: _5, recentViewers: _6) } else { return nil } } - public static func parse_updateStickerSetsOrder(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Int64]? + + } +} +public extension Api { + enum TextWithEntities: TypeConstructorDescription { + case textWithEntities(text: String, entities: [Api.MessageEntity]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .textWithEntities(let text, let entities): + if boxed { + buffer.appendInt32(1964978502) + } + serializeString(text, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities.count)) + for item in entities { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .textWithEntities(let text, let entities): + return ("textWithEntities", [("text", text as Any), ("entities", entities as Any)]) + } + } + + public static func parse_textWithEntities(_ reader: BufferReader) -> TextWithEntities? { + var _1: String? + _1 = parseString(reader) + var _2: [Api.MessageEntity]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateStickerSetsOrder(flags: _1!, order: _2!) - } - else { - return nil - } - } - public static func parse_updateStoriesStealthMode(_ reader: BufferReader) -> Update? { - var _1: Api.StoriesStealthMode? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.StoriesStealthMode - } - let _c1 = _1 != nil - if _c1 { - return Api.Update.updateStoriesStealthMode(stealthMode: _1!) - } - else { - return nil - } - } - public static func parse_updateStory(_ reader: BufferReader) -> Update? { - var _1: Api.Peer? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _2: Api.StoryItem? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.StoryItem + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) } let _c1 = _1 != nil let _c2 = _2 != nil if _c1 && _c2 { - return Api.Update.updateStory(peer: _1!, story: _2!) + return Api.TextWithEntities.textWithEntities(text: _1!, entities: _2!) } else { return nil } } - public static func parse_updateStoryID(_ reader: BufferReader) -> Update? { + + } +} +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() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateStoryID(id: _1!, randomId: _2!) - } - else { - return nil - } - } - public static func parse_updateTheme(_ reader: BufferReader) -> Update? { - var _1: Api.Theme? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Theme - } - let _c1 = _1 != nil - if _c1 { - return Api.Update.updateTheme(theme: _1!) - } - else { - return nil - } - } - public static func parse_updateTranscribedAudio(_ reader: BufferReader) -> Update? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.Peer? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _3: Int32? - _3 = reader.readInt32() - var _4: Int64? - _4 = 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 - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Update.updateTranscribedAudio(flags: _1!, peer: _2!, msgId: _3!, transcriptionId: _4!, text: _5!) - } - else { - return nil - } - } - public static func parse_updateUser(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - let _c1 = _1 != nil - if _c1 { - return Api.Update.updateUser(userId: _1!) + 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 static func parse_updateUserEmojiStatus(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Api.EmojiStatus? + + } +} +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.EmojiStatus - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateUserEmojiStatus(userId: _1!, emojiStatus: _2!) - } - else { - return nil - } - } - public static func parse_updateUserName(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - var _2: String? - _2 = parseString(reader) - var _3: String? - _3 = parseString(reader) - var _4: [Api.Username]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Username.self) + _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 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateUserName(userId: _1!, firstName: _2!, lastName: _3!, usernames: _4!) - } - else { - return nil - } - } - public static func parse_updateUserPhone(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - var _2: String? - _2 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateUserPhone(userId: _1!, phone: _2!) - } - else { - return nil - } - } - public static func parse_updateUserStatus(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Api.UserStatus? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.UserStatus - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateUserStatus(userId: _1!, status: _2!) + 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 static func parse_updateUserTyping(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Api.SendMessageAction? + + } +} +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() { - _2 = Api.parse(reader, signature: signature) as? Api.SendMessageAction + _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.Update.updateUserTyping(userId: _1!, action: _2!) - } - else { - return nil - } - } - public static func parse_updateWebPage(_ reader: BufferReader) -> Update? { - var _1: Api.WebPage? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.WebPage - } - 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.Update.updateWebPage(webpage: _1!, pts: _2!, ptsCount: _3!) - } - else { - return nil - } - } - public static func parse_updateWebViewResultSent(_ reader: BufferReader) -> Update? { - var _1: Int64? - _1 = reader.readInt64() - let _c1 = _1 != nil - if _c1 { - return Api.Update.updateWebViewResultSent(queryId: _1!) + return Api.TopPeer.topPeer(peer: _1!, rating: _2!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api22.swift b/submodules/TelegramApi/Sources/Api22.swift index b99155ae2cf..a6f81793c12 100644 --- a/submodules/TelegramApi/Sources/Api22.swift +++ b/submodules/TelegramApi/Sources/Api22.swift @@ -1,130 +1,61 @@ public extension Api { - indirect enum Updates: TypeConstructorDescription { - case updateShort(update: Api.Update, date: Int32) - case updateShortChatMessage(flags: Int32, id: Int32, fromId: Int64, chatId: Int64, message: String, pts: Int32, ptsCount: Int32, date: Int32, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, replyTo: Api.MessageReplyHeader?, entities: [Api.MessageEntity]?, ttlPeriod: Int32?) - case updateShortMessage(flags: Int32, id: Int32, userId: Int64, message: String, pts: Int32, ptsCount: Int32, date: Int32, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, replyTo: Api.MessageReplyHeader?, entities: [Api.MessageEntity]?, ttlPeriod: Int32?) - case updateShortSentMessage(flags: Int32, id: Int32, pts: Int32, ptsCount: Int32, date: Int32, media: Api.MessageMedia?, entities: [Api.MessageEntity]?, ttlPeriod: Int32?) - case updates(updates: [Api.Update], users: [Api.User], chats: [Api.Chat], date: Int32, seq: Int32) - case updatesCombined(updates: [Api.Update], users: [Api.User], chats: [Api.Chat], date: Int32, seqStart: Int32, seq: Int32) - case updatesTooLong + enum TopPeerCategory: TypeConstructorDescription { + 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 .updateShort(let update, let date): + case .topPeerCategoryBotsInline: if boxed { - buffer.appendInt32(2027216577) + buffer.appendInt32(344356834) } - update.serialize(buffer, true) - serializeInt32(date, buffer: buffer, boxed: false) + break - case .updateShortChatMessage(let flags, let id, let fromId, let chatId, let message, let pts, let ptsCount, let date, let fwdFrom, let viaBotId, let replyTo, let entities, let ttlPeriod): + case .topPeerCategoryBotsPM: if boxed { - buffer.appendInt32(1299050149) + buffer.appendInt32(-1419371685) } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(id, buffer: buffer, boxed: false) - serializeInt64(fromId, buffer: buffer, boxed: false) - serializeInt64(chatId, buffer: buffer, boxed: false) - serializeString(message, buffer: buffer, boxed: false) - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {fwdFrom!.serialize(buffer, true)} - if Int(flags) & Int(1 << 11) != 0 {serializeInt64(viaBotId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {replyTo!.serialize(buffer, true)} - if Int(flags) & Int(1 << 7) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities!.count)) - for item in entities! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 25) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)} + break - case .updateShortMessage(let flags, let id, let userId, let message, let pts, let ptsCount, let date, let fwdFrom, let viaBotId, let replyTo, let entities, let ttlPeriod): + case .topPeerCategoryChannels: if boxed { - buffer.appendInt32(826001400) + buffer.appendInt32(371037736) } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(id, buffer: buffer, boxed: false) - serializeInt64(userId, buffer: buffer, boxed: false) - serializeString(message, buffer: buffer, boxed: false) - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {fwdFrom!.serialize(buffer, true)} - if Int(flags) & Int(1 << 11) != 0 {serializeInt64(viaBotId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {replyTo!.serialize(buffer, true)} - if Int(flags) & Int(1 << 7) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities!.count)) - for item in entities! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 25) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)} + break - case .updateShortSentMessage(let flags, let id, let pts, let ptsCount, let date, let media, let entities, let ttlPeriod): + case .topPeerCategoryCorrespondents: if boxed { - buffer.appendInt32(-1877614335) + buffer.appendInt32(104314861) } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(id, buffer: buffer, boxed: false) - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 9) != 0 {media!.serialize(buffer, true)} - if Int(flags) & Int(1 << 7) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities!.count)) - for item in entities! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 25) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)} + break - case .updates(let updates, let users, let chats, let date, let seq): + case .topPeerCategoryForwardChats: if boxed { - buffer.appendInt32(1957577280) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(updates.count)) - for item in updates { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + buffer.appendInt32(-68239120) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt32(seq, buffer: buffer, boxed: false) + break - case .updatesCombined(let updates, let users, let chats, let date, let seqStart, let seq): + case .topPeerCategoryForwardUsers: if boxed { - buffer.appendInt32(1918567619) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(updates.count)) - for item in updates { - item.serialize(buffer, true) + buffer.appendInt32(-1472172887) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) + + break + case .topPeerCategoryGroups: + if boxed { + buffer.appendInt32(-1122524854) } - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt32(seqStart, buffer: buffer, boxed: false) - serializeInt32(seq, buffer: buffer, boxed: false) + break - case .updatesTooLong: + case .topPeerCategoryPhoneCalls: if boxed { - buffer.appendInt32(-484987010) + buffer.appendInt32(511092620) } break @@ -133,315 +64,96 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .updateShort(let update, let date): - return ("updateShort", [("update", update as Any), ("date", date as Any)]) - case .updateShortChatMessage(let flags, let id, let fromId, let chatId, let message, let pts, let ptsCount, let date, let fwdFrom, let viaBotId, let replyTo, let entities, let ttlPeriod): - return ("updateShortChatMessage", [("flags", flags as Any), ("id", id as Any), ("fromId", fromId as Any), ("chatId", chatId as Any), ("message", message as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any), ("date", date as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("replyTo", replyTo as Any), ("entities", entities as Any), ("ttlPeriod", ttlPeriod as Any)]) - case .updateShortMessage(let flags, let id, let userId, let message, let pts, let ptsCount, let date, let fwdFrom, let viaBotId, let replyTo, let entities, let ttlPeriod): - return ("updateShortMessage", [("flags", flags as Any), ("id", id as Any), ("userId", userId as Any), ("message", message as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any), ("date", date as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("replyTo", replyTo as Any), ("entities", entities as Any), ("ttlPeriod", ttlPeriod as Any)]) - case .updateShortSentMessage(let flags, let id, let pts, let ptsCount, let date, let media, let entities, let ttlPeriod): - return ("updateShortSentMessage", [("flags", flags as Any), ("id", id as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any), ("date", date as Any), ("media", media as Any), ("entities", entities as Any), ("ttlPeriod", ttlPeriod as Any)]) - case .updates(let updates, let users, let chats, let date, let seq): - return ("updates", [("updates", updates as Any), ("users", users as Any), ("chats", chats as Any), ("date", date as Any), ("seq", seq as Any)]) - case .updatesCombined(let updates, let users, let chats, let date, let seqStart, let seq): - return ("updatesCombined", [("updates", updates as Any), ("users", users as Any), ("chats", chats as Any), ("date", date as Any), ("seqStart", seqStart as Any), ("seq", seq as Any)]) - case .updatesTooLong: - return ("updatesTooLong", []) + 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_updateShort(_ reader: BufferReader) -> Updates? { - var _1: Api.Update? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Update - } - var _2: Int32? - _2 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Updates.updateShort(update: _1!, date: _2!) - } - else { - return nil - } + public static func parse_topPeerCategoryBotsInline(_ reader: BufferReader) -> TopPeerCategory? { + return Api.TopPeerCategory.topPeerCategoryBotsInline } - public static func parse_updateShortChatMessage(_ reader: BufferReader) -> Updates? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int64? - _3 = reader.readInt64() - var _4: Int64? - _4 = reader.readInt64() - var _5: String? - _5 = parseString(reader) - var _6: Int32? - _6 = reader.readInt32() - var _7: Int32? - _7 = reader.readInt32() - var _8: Int32? - _8 = reader.readInt32() - var _9: Api.MessageFwdHeader? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _9 = Api.parse(reader, signature: signature) as? Api.MessageFwdHeader - } } - var _10: Int64? - if Int(_1!) & Int(1 << 11) != 0 {_10 = reader.readInt64() } - var _11: Api.MessageReplyHeader? - if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { - _11 = Api.parse(reader, signature: signature) as? Api.MessageReplyHeader - } } - var _12: [Api.MessageEntity]? - if Int(_1!) & Int(1 << 7) != 0 {if let _ = reader.readInt32() { - _12 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) - } } - var _13: Int32? - if Int(_1!) & Int(1 << 25) != 0 {_13 = 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 - let _c8 = _8 != nil - let _c9 = (Int(_1!) & Int(1 << 2) == 0) || _9 != nil - let _c10 = (Int(_1!) & Int(1 << 11) == 0) || _10 != nil - let _c11 = (Int(_1!) & Int(1 << 3) == 0) || _11 != nil - let _c12 = (Int(_1!) & Int(1 << 7) == 0) || _12 != nil - let _c13 = (Int(_1!) & Int(1 << 25) == 0) || _13 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 { - return Api.Updates.updateShortChatMessage(flags: _1!, id: _2!, fromId: _3!, chatId: _4!, message: _5!, pts: _6!, ptsCount: _7!, date: _8!, fwdFrom: _9, viaBotId: _10, replyTo: _11, entities: _12, ttlPeriod: _13) - } - else { - return nil - } + public static func parse_topPeerCategoryBotsPM(_ reader: BufferReader) -> TopPeerCategory? { + return Api.TopPeerCategory.topPeerCategoryBotsPM } - public static func parse_updateShortMessage(_ reader: BufferReader) -> Updates? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int64? - _3 = reader.readInt64() - var _4: String? - _4 = parseString(reader) - var _5: Int32? - _5 = reader.readInt32() - var _6: Int32? - _6 = reader.readInt32() - var _7: Int32? - _7 = reader.readInt32() - var _8: Api.MessageFwdHeader? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.MessageFwdHeader - } } - var _9: Int64? - if Int(_1!) & Int(1 << 11) != 0 {_9 = reader.readInt64() } - var _10: Api.MessageReplyHeader? - if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { - _10 = Api.parse(reader, signature: signature) as? Api.MessageReplyHeader - } } - var _11: [Api.MessageEntity]? - if Int(_1!) & Int(1 << 7) != 0 {if let _ = reader.readInt32() { - _11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) - } } - var _12: Int32? - if Int(_1!) & Int(1 << 25) != 0 {_12 = 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 - let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil - let _c9 = (Int(_1!) & Int(1 << 11) == 0) || _9 != nil - let _c10 = (Int(_1!) & Int(1 << 3) == 0) || _10 != nil - let _c11 = (Int(_1!) & Int(1 << 7) == 0) || _11 != nil - let _c12 = (Int(_1!) & Int(1 << 25) == 0) || _12 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 { - return Api.Updates.updateShortMessage(flags: _1!, id: _2!, userId: _3!, message: _4!, pts: _5!, ptsCount: _6!, date: _7!, fwdFrom: _8, viaBotId: _9, replyTo: _10, entities: _11, ttlPeriod: _12) - } - else { - return nil - } + public static func parse_topPeerCategoryChannels(_ reader: BufferReader) -> TopPeerCategory? { + return Api.TopPeerCategory.topPeerCategoryChannels } - public static func parse_updateShortSentMessage(_ reader: BufferReader) -> Updates? { - 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: Api.MessageMedia? - if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.MessageMedia - } } - var _7: [Api.MessageEntity]? - if Int(_1!) & Int(1 << 7) != 0 {if let _ = reader.readInt32() { - _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) - } } - var _8: Int32? - if Int(_1!) & Int(1 << 25) != 0 {_8 = 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 << 9) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 7) == 0) || _7 != nil - let _c8 = (Int(_1!) & Int(1 << 25) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.Updates.updateShortSentMessage(flags: _1!, id: _2!, pts: _3!, ptsCount: _4!, date: _5!, media: _6, entities: _7, ttlPeriod: _8) - } - else { - return nil - } + public static func parse_topPeerCategoryCorrespondents(_ reader: BufferReader) -> TopPeerCategory? { + return Api.TopPeerCategory.topPeerCategoryCorrespondents } - public static func parse_updates(_ reader: BufferReader) -> Updates? { - var _1: [Api.Update]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Update.self) - } - var _2: [Api.User]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - var _3: [Api.Chat]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _4: Int32? - _4 = reader.readInt32() - var _5: Int32? - _5 = reader.readInt32() - 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.Updates.updates(updates: _1!, users: _2!, chats: _3!, date: _4!, seq: _5!) - } - else { - return nil - } + public static func parse_topPeerCategoryForwardChats(_ reader: BufferReader) -> TopPeerCategory? { + return Api.TopPeerCategory.topPeerCategoryForwardChats } - public static func parse_updatesCombined(_ reader: BufferReader) -> Updates? { - var _1: [Api.Update]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Update.self) - } - var _2: [Api.User]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - var _3: [Api.Chat]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _4: Int32? - _4 = reader.readInt32() - var _5: Int32? - _5 = reader.readInt32() - var _6: Int32? - _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 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.Updates.updatesCombined(updates: _1!, users: _2!, chats: _3!, date: _4!, seqStart: _5!, seq: _6!) - } - else { - return nil - } + public static func parse_topPeerCategoryForwardUsers(_ reader: BufferReader) -> TopPeerCategory? { + return Api.TopPeerCategory.topPeerCategoryForwardUsers } - public static func parse_updatesTooLong(_ reader: BufferReader) -> Updates? { - return Api.Updates.updatesTooLong + 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 UrlAuthResult: TypeConstructorDescription { - case urlAuthResultAccepted(url: String) - case urlAuthResultDefault - case urlAuthResultRequest(flags: Int32, bot: Api.User, domain: String) + enum TopPeerCategoryPeers: TypeConstructorDescription { + case topPeerCategoryPeers(category: Api.TopPeerCategory, count: Int32, peers: [Api.TopPeer]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .urlAuthResultAccepted(let url): - if boxed { - buffer.appendInt32(-1886646706) - } - serializeString(url, buffer: buffer, boxed: false) - break - case .urlAuthResultDefault: + case .topPeerCategoryPeers(let category, let count, let peers): if boxed { - buffer.appendInt32(-1445536993) + buffer.appendInt32(-75283823) } - - break - case .urlAuthResultRequest(let flags, let bot, let domain): - if boxed { - buffer.appendInt32(-1831650802) + category.serialize(buffer, true) + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peers.count)) + for item in peers { + item.serialize(buffer, true) } - serializeInt32(flags, buffer: buffer, boxed: false) - bot.serialize(buffer, true) - serializeString(domain, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .urlAuthResultAccepted(let url): - return ("urlAuthResultAccepted", [("url", url as Any)]) - case .urlAuthResultDefault: - return ("urlAuthResultDefault", []) - case .urlAuthResultRequest(let flags, let bot, let domain): - return ("urlAuthResultRequest", [("flags", flags as Any), ("bot", bot as Any), ("domain", domain as Any)]) + case .topPeerCategoryPeers(let category, let count, let peers): + return ("topPeerCategoryPeers", [("category", category as Any), ("count", count as Any), ("peers", peers as Any)]) } } - public static func parse_urlAuthResultAccepted(_ reader: BufferReader) -> UrlAuthResult? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.UrlAuthResult.urlAuthResultAccepted(url: _1!) - } - else { - return nil - } - } - public static func parse_urlAuthResultDefault(_ reader: BufferReader) -> UrlAuthResult? { - return Api.UrlAuthResult.urlAuthResultDefault - } - public static func parse_urlAuthResultRequest(_ reader: BufferReader) -> UrlAuthResult? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.User? + public static func parse_topPeerCategoryPeers(_ reader: BufferReader) -> TopPeerCategoryPeers? { + var _1: Api.TopPeerCategory? if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.User + _1 = Api.parse(reader, signature: signature) as? Api.TopPeerCategory + } + var _2: Int32? + _2 = reader.readInt32() + var _3: [Api.TopPeer]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.TopPeer.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.UrlAuthResult.urlAuthResultRequest(flags: _1!, bot: _2!, domain: _3!) + return Api.TopPeerCategoryPeers.topPeerCategoryPeers(category: _1!, count: _2!, peers: _3!) } else { return nil @@ -451,1125 +163,3479 @@ public extension Api { } } public extension Api { - enum User: TypeConstructorDescription { - case user(flags: Int32, flags2: Int32, id: Int64, accessHash: Int64?, firstName: String?, lastName: String?, username: String?, phone: String?, photo: Api.UserProfilePhoto?, status: Api.UserStatus?, botInfoVersion: Int32?, restrictionReason: [Api.RestrictionReason]?, botInlinePlaceholder: String?, langCode: String?, emojiStatus: Api.EmojiStatus?, usernames: [Api.Username]?, storiesMaxId: Int32?, color: Int32?, backgroundEmojiId: Int64?) - case userEmpty(id: Int64) + indirect enum Update: TypeConstructorDescription { + case updateAttachMenuBots + case updateAutoSaveSettings + case updateBotCallbackQuery(flags: Int32, queryId: Int64, userId: Int64, peer: Api.Peer, msgId: Int32, chatInstance: Int64, data: Buffer?, gameShortName: String?) + case updateBotChatBoost(peer: Api.Peer, boost: Api.Boost, qts: Int32) + case updateBotChatInviteRequester(peer: Api.Peer, date: Int32, userId: Int64, about: String, invite: Api.ExportedChatInvite, qts: Int32) + case updateBotCommands(peer: Api.Peer, botId: Int64, commands: [Api.BotCommand]) + case updateBotInlineQuery(flags: Int32, queryId: Int64, userId: Int64, query: String, geo: Api.GeoPoint?, peerType: Api.InlineQueryPeerType?, offset: String) + case updateBotInlineSend(flags: Int32, userId: Int64, query: String, geo: Api.GeoPoint?, id: String, msgId: Api.InputBotInlineMessageID?) + case updateBotMenuButton(botId: Int64, button: Api.BotMenuButton) + case updateBotPrecheckoutQuery(flags: Int32, queryId: Int64, userId: Int64, payload: Buffer, info: Api.PaymentRequestedInfo?, shippingOptionId: String?, currency: String, totalAmount: Int64) + case updateBotShippingQuery(queryId: Int64, userId: Int64, payload: Buffer, shippingAddress: Api.PostAddress) + case updateBotStopped(userId: Int64, date: Int32, stopped: Api.Bool, qts: Int32) + case updateBotWebhookJSON(data: Api.DataJSON) + case updateBotWebhookJSONQuery(queryId: Int64, data: Api.DataJSON, timeout: Int32) + case updateChannel(channelId: Int64) + case updateChannelAvailableMessages(channelId: Int64, availableMinId: Int32) + case updateChannelMessageForwards(channelId: Int64, id: Int32, forwards: Int32) + case updateChannelMessageViews(channelId: Int64, id: Int32, views: Int32) + case updateChannelParticipant(flags: Int32, channelId: Int64, date: Int32, actorId: Int64, userId: Int64, prevParticipant: Api.ChannelParticipant?, newParticipant: Api.ChannelParticipant?, invite: Api.ExportedChatInvite?, qts: Int32) + case updateChannelPinnedTopic(flags: Int32, channelId: Int64, topicId: Int32) + case updateChannelPinnedTopics(flags: Int32, channelId: Int64, order: [Int32]?) + case updateChannelReadMessagesContents(flags: Int32, channelId: Int64, topMsgId: Int32?, messages: [Int32]) + case updateChannelTooLong(flags: Int32, channelId: Int64, pts: Int32?) + case updateChannelUserTyping(flags: Int32, channelId: Int64, topMsgId: Int32?, fromId: Api.Peer, action: Api.SendMessageAction) + case updateChannelViewForumAsMessages(channelId: Int64, enabled: Api.Bool) + case updateChannelWebPage(channelId: Int64, webpage: Api.WebPage, pts: Int32, ptsCount: Int32) + case updateChat(chatId: Int64) + case updateChatDefaultBannedRights(peer: Api.Peer, defaultBannedRights: Api.ChatBannedRights, version: Int32) + case updateChatParticipant(flags: Int32, chatId: Int64, date: Int32, actorId: Int64, userId: Int64, prevParticipant: Api.ChatParticipant?, newParticipant: Api.ChatParticipant?, invite: Api.ExportedChatInvite?, qts: Int32) + case updateChatParticipantAdd(chatId: Int64, userId: Int64, inviterId: Int64, date: Int32, version: Int32) + case updateChatParticipantAdmin(chatId: Int64, userId: Int64, isAdmin: Api.Bool, version: Int32) + case updateChatParticipantDelete(chatId: Int64, userId: Int64, version: Int32) + case updateChatParticipants(participants: Api.ChatParticipants) + case updateChatUserTyping(chatId: Int64, fromId: Api.Peer, action: Api.SendMessageAction) + case updateConfig + case updateContactsReset + case updateDcOptions(dcOptions: [Api.DcOption]) + case updateDeleteChannelMessages(channelId: Int64, messages: [Int32], pts: Int32, ptsCount: Int32) + case updateDeleteMessages(messages: [Int32], pts: Int32, ptsCount: Int32) + case updateDeleteScheduledMessages(peer: Api.Peer, messages: [Int32]) + case updateDialogFilter(flags: Int32, id: Int32, filter: Api.DialogFilter?) + case updateDialogFilterOrder(order: [Int32]) + case updateDialogFilters + case updateDialogPinned(flags: Int32, folderId: Int32?, peer: Api.DialogPeer) + case updateDialogUnreadMark(flags: Int32, peer: Api.DialogPeer) + case updateDraftMessage(flags: Int32, peer: Api.Peer, topMsgId: Int32?, draft: Api.DraftMessage) + case updateEditChannelMessage(message: Api.Message, pts: Int32, ptsCount: Int32) + case updateEditMessage(message: Api.Message, pts: Int32, ptsCount: Int32) + case updateEncryptedChatTyping(chatId: Int32) + case updateEncryptedMessagesRead(chatId: Int32, maxDate: Int32, date: Int32) + case updateEncryption(chat: Api.EncryptedChat, date: Int32) + case updateFavedStickers + case updateFolderPeers(folderPeers: [Api.FolderPeer], pts: Int32, ptsCount: Int32) + case updateGeoLiveViewed(peer: Api.Peer, msgId: Int32) + case updateGroupCall(chatId: Int64, call: Api.GroupCall) + case updateGroupCallConnection(flags: Int32, params: Api.DataJSON) + case updateGroupCallParticipants(call: Api.InputGroupCall, participants: [Api.GroupCallParticipant], version: Int32) + case updateGroupInvitePrivacyForbidden(userId: Int64) + case updateInlineBotCallbackQuery(flags: Int32, queryId: Int64, userId: Int64, msgId: Api.InputBotInlineMessageID, chatInstance: Int64, data: Buffer?, gameShortName: String?) + case updateLangPack(difference: Api.LangPackDifference) + case updateLangPackTooLong(langCode: String) + case updateLoginToken + case updateMessageExtendedMedia(peer: Api.Peer, msgId: Int32, extendedMedia: Api.MessageExtendedMedia) + case updateMessageID(id: Int32, randomId: Int64) + case updateMessagePoll(flags: Int32, pollId: Int64, poll: Api.Poll?, results: Api.PollResults) + case updateMessagePollVote(pollId: Int64, peer: Api.Peer, options: [Buffer], qts: Int32) + case updateMessageReactions(flags: Int32, peer: Api.Peer, msgId: Int32, topMsgId: Int32?, reactions: Api.MessageReactions) + case updateMoveStickerSetToTop(flags: Int32, stickerset: Int64) + case updateNewAuthorization(flags: Int32, hash: Int64, date: Int32?, device: String?, location: String?) + case updateNewChannelMessage(message: Api.Message, pts: Int32, ptsCount: Int32) + case updateNewEncryptedMessage(message: Api.EncryptedMessage, qts: Int32) + case updateNewMessage(message: Api.Message, pts: Int32, ptsCount: Int32) + case updateNewScheduledMessage(message: Api.Message) + case updateNewStickerSet(stickerset: Api.messages.StickerSet) + case updateNotifySettings(peer: Api.NotifyPeer, notifySettings: Api.PeerNotifySettings) + case updatePeerBlocked(flags: Int32, peerId: Api.Peer) + case updatePeerHistoryTTL(flags: Int32, peer: Api.Peer, ttlPeriod: Int32?) + case updatePeerLocated(peers: [Api.PeerLocated]) + case updatePeerSettings(peer: Api.Peer, settings: Api.PeerSettings) + case updatePeerWallpaper(flags: Int32, peer: Api.Peer, wallpaper: Api.WallPaper?) + case updatePendingJoinRequests(peer: Api.Peer, requestsPending: Int32, recentRequesters: [Int64]) + case updatePhoneCall(phoneCall: Api.PhoneCall) + case updatePhoneCallSignalingData(phoneCallId: Int64, data: Buffer) + case updatePinnedChannelMessages(flags: Int32, channelId: Int64, messages: [Int32], pts: Int32, ptsCount: Int32) + case updatePinnedDialogs(flags: Int32, folderId: Int32?, order: [Api.DialogPeer]?) + case updatePinnedMessages(flags: Int32, peer: Api.Peer, messages: [Int32], pts: Int32, ptsCount: Int32) + case updatePrivacy(key: Api.PrivacyKey, rules: [Api.PrivacyRule]) + case updatePtsChanged + case updateReadChannelDiscussionInbox(flags: Int32, channelId: Int64, topMsgId: Int32, readMaxId: Int32, broadcastId: Int64?, broadcastPost: Int32?) + case updateReadChannelDiscussionOutbox(channelId: Int64, topMsgId: Int32, readMaxId: Int32) + case updateReadChannelInbox(flags: Int32, folderId: Int32?, channelId: Int64, maxId: Int32, stillUnreadCount: Int32, pts: Int32) + case updateReadChannelOutbox(channelId: Int64, maxId: Int32) + case updateReadFeaturedEmojiStickers + case updateReadFeaturedStickers + case updateReadHistoryInbox(flags: Int32, folderId: Int32?, peer: Api.Peer, maxId: Int32, stillUnreadCount: Int32, pts: Int32, ptsCount: Int32) + case updateReadHistoryOutbox(peer: Api.Peer, maxId: Int32, pts: Int32, ptsCount: Int32) + case updateReadMessagesContents(flags: Int32, messages: [Int32], pts: Int32, ptsCount: Int32, date: Int32?) + case updateReadStories(peer: Api.Peer, maxId: Int32) + case updateRecentEmojiStatuses + case updateRecentReactions + case updateRecentStickers + case updateSavedGifs + case updateSavedRingtones + case updateSentStoryReaction(peer: Api.Peer, storyId: Int32, reaction: Api.Reaction) + case updateServiceNotification(flags: Int32, inboxDate: Int32?, type: String, message: String, media: Api.MessageMedia, entities: [Api.MessageEntity]) + case updateStickerSets(flags: Int32) + case updateStickerSetsOrder(flags: Int32, order: [Int64]) + case updateStoriesStealthMode(stealthMode: Api.StoriesStealthMode) + case updateStory(peer: Api.Peer, story: Api.StoryItem) + case updateStoryID(id: Int32, randomId: Int64) + case updateTheme(theme: Api.Theme) + case updateTranscribedAudio(flags: Int32, peer: Api.Peer, msgId: Int32, transcriptionId: Int64, text: String) + case updateUser(userId: Int64) + case updateUserEmojiStatus(userId: Int64, emojiStatus: Api.EmojiStatus) + case updateUserName(userId: Int64, firstName: String, lastName: String, usernames: [Api.Username]) + case updateUserPhone(userId: Int64, phone: String) + case updateUserStatus(userId: Int64, status: Api.UserStatus) + case updateUserTyping(userId: Int64, action: Api.SendMessageAction) + case updateWebPage(webpage: Api.WebPage, pts: Int32, ptsCount: Int32) + case updateWebViewResultSent(queryId: Int64) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .user(let flags, let flags2, let id, let accessHash, let firstName, let lastName, let username, let phone, let photo, let status, let botInfoVersion, let restrictionReason, let botInlinePlaceholder, let langCode, let emojiStatus, let usernames, let storiesMaxId, let color, let backgroundEmojiId): + case .updateAttachMenuBots: + if boxed { + buffer.appendInt32(397910539) + } + + break + case .updateAutoSaveSettings: + if boxed { + buffer.appendInt32(-335171433) + } + + break + case .updateBotCallbackQuery(let flags, let queryId, let userId, let peer, let msgId, let chatInstance, let data, let gameShortName): if boxed { - buffer.appendInt32(-346018011) + buffer.appendInt32(-1177566067) } serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(flags2, buffer: buffer, boxed: false) - serializeInt64(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt64(accessHash!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeString(firstName!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(lastName!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {serializeString(username!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {serializeString(phone!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 5) != 0 {photo!.serialize(buffer, true)} - if Int(flags) & Int(1 << 6) != 0 {status!.serialize(buffer, true)} - if Int(flags) & Int(1 << 14) != 0 {serializeInt32(botInfoVersion!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 18) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(restrictionReason!.count)) - for item in restrictionReason! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 19) != 0 {serializeString(botInlinePlaceholder!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 22) != 0 {serializeString(langCode!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 30) != 0 {emojiStatus!.serialize(buffer, true)} - if Int(flags2) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(usernames!.count)) - for item in usernames! { + serializeInt64(queryId, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + serializeInt64(chatInstance, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeBytes(data!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(gameShortName!, buffer: buffer, boxed: false)} + break + case .updateBotChatBoost(let peer, let boost, let qts): + if boxed { + buffer.appendInt32(-1873947492) + } + peer.serialize(buffer, true) + boost.serialize(buffer, true) + serializeInt32(qts, buffer: buffer, boxed: false) + break + case .updateBotChatInviteRequester(let peer, let date, let userId, let about, let invite, let qts): + if boxed { + buffer.appendInt32(299870598) + } + peer.serialize(buffer, true) + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + serializeString(about, buffer: buffer, boxed: false) + invite.serialize(buffer, true) + serializeInt32(qts, buffer: buffer, boxed: false) + break + case .updateBotCommands(let peer, let botId, let commands): + if boxed { + buffer.appendInt32(1299263278) + } + peer.serialize(buffer, true) + serializeInt64(botId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(commands.count)) + for item in commands { item.serialize(buffer, true) - }} - if Int(flags2) & Int(1 << 5) != 0 {serializeInt32(storiesMaxId!, buffer: buffer, boxed: false)} - if Int(flags2) & Int(1 << 7) != 0 {serializeInt32(color!, buffer: buffer, boxed: false)} - if Int(flags2) & Int(1 << 6) != 0 {serializeInt64(backgroundEmojiId!, buffer: buffer, boxed: false)} + } break - case .userEmpty(let id): + case .updateBotInlineQuery(let flags, let queryId, let userId, let query, let geo, let peerType, let offset): if boxed { - buffer.appendInt32(-742634630) + buffer.appendInt32(1232025500) } - serializeInt64(id, buffer: buffer, boxed: false) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(queryId, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + serializeString(query, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {geo!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {peerType!.serialize(buffer, true)} + serializeString(offset, buffer: buffer, boxed: false) break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .user(let flags, let flags2, let id, let accessHash, let firstName, let lastName, let username, let phone, let photo, let status, let botInfoVersion, let restrictionReason, let botInlinePlaceholder, let langCode, let emojiStatus, let usernames, let storiesMaxId, let color, let backgroundEmojiId): - return ("user", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("firstName", firstName as Any), ("lastName", lastName as Any), ("username", username as Any), ("phone", phone as Any), ("photo", photo as Any), ("status", status as Any), ("botInfoVersion", botInfoVersion as Any), ("restrictionReason", restrictionReason as Any), ("botInlinePlaceholder", botInlinePlaceholder as Any), ("langCode", langCode as Any), ("emojiStatus", emojiStatus as Any), ("usernames", usernames as Any), ("storiesMaxId", storiesMaxId as Any), ("color", color as Any), ("backgroundEmojiId", backgroundEmojiId as Any)]) - case .userEmpty(let id): - return ("userEmpty", [("id", id as Any)]) - } - } - - public static func parse_user(_ reader: BufferReader) -> User? { - var _1: Int32? - _1 = reader.readInt32() + case .updateBotInlineSend(let flags, let userId, let query, let geo, let id, let msgId): + if boxed { + buffer.appendInt32(317794823) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + serializeString(query, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {geo!.serialize(buffer, true)} + serializeString(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {msgId!.serialize(buffer, true)} + break + case .updateBotMenuButton(let botId, let button): + if boxed { + buffer.appendInt32(347625491) + } + serializeInt64(botId, buffer: buffer, boxed: false) + button.serialize(buffer, true) + break + case .updateBotPrecheckoutQuery(let flags, let queryId, let userId, let payload, let info, let shippingOptionId, let currency, let totalAmount): + if boxed { + buffer.appendInt32(-1934976362) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(queryId, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + serializeBytes(payload, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {info!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(shippingOptionId!, buffer: buffer, boxed: false)} + serializeString(currency, buffer: buffer, boxed: false) + serializeInt64(totalAmount, buffer: buffer, boxed: false) + break + case .updateBotShippingQuery(let queryId, let userId, let payload, let shippingAddress): + if boxed { + buffer.appendInt32(-1246823043) + } + serializeInt64(queryId, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + serializeBytes(payload, buffer: buffer, boxed: false) + shippingAddress.serialize(buffer, true) + break + case .updateBotStopped(let userId, let date, let stopped, let qts): + if boxed { + buffer.appendInt32(-997782967) + } + serializeInt64(userId, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + stopped.serialize(buffer, true) + serializeInt32(qts, buffer: buffer, boxed: false) + break + case .updateBotWebhookJSON(let data): + if boxed { + buffer.appendInt32(-2095595325) + } + data.serialize(buffer, true) + break + case .updateBotWebhookJSONQuery(let queryId, let data, let timeout): + if boxed { + buffer.appendInt32(-1684914010) + } + serializeInt64(queryId, buffer: buffer, boxed: false) + data.serialize(buffer, true) + serializeInt32(timeout, buffer: buffer, boxed: false) + break + case .updateChannel(let channelId): + if boxed { + buffer.appendInt32(1666927625) + } + serializeInt64(channelId, buffer: buffer, boxed: false) + break + case .updateChannelAvailableMessages(let channelId, let availableMinId): + if boxed { + buffer.appendInt32(-1304443240) + } + serializeInt64(channelId, buffer: buffer, boxed: false) + serializeInt32(availableMinId, buffer: buffer, boxed: false) + break + case .updateChannelMessageForwards(let channelId, let id, let forwards): + if boxed { + buffer.appendInt32(-761649164) + } + serializeInt64(channelId, buffer: buffer, boxed: false) + serializeInt32(id, buffer: buffer, boxed: false) + serializeInt32(forwards, buffer: buffer, boxed: false) + break + case .updateChannelMessageViews(let channelId, let id, let views): + if boxed { + buffer.appendInt32(-232346616) + } + serializeInt64(channelId, buffer: buffer, boxed: false) + serializeInt32(id, buffer: buffer, boxed: false) + serializeInt32(views, buffer: buffer, boxed: false) + break + case .updateChannelParticipant(let flags, let channelId, let date, let actorId, let userId, let prevParticipant, let newParticipant, let invite, let qts): + if boxed { + buffer.appendInt32(-1738720581) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(channelId, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt64(actorId, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {prevParticipant!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {newParticipant!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {invite!.serialize(buffer, true)} + serializeInt32(qts, buffer: buffer, boxed: false) + break + case .updateChannelPinnedTopic(let flags, let channelId, let topicId): + if boxed { + buffer.appendInt32(422509539) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(channelId, buffer: buffer, boxed: false) + serializeInt32(topicId, buffer: buffer, boxed: false) + break + case .updateChannelPinnedTopics(let flags, let channelId, let order): + if boxed { + buffer.appendInt32(-31881726) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(channelId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order!.count)) + for item in order! { + serializeInt32(item, buffer: buffer, boxed: false) + }} + break + case .updateChannelReadMessagesContents(let flags, let channelId, let topMsgId, let messages): + if boxed { + buffer.appendInt32(-366410403) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(channelId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + serializeInt32(item, buffer: buffer, boxed: false) + } + break + case .updateChannelTooLong(let flags, let channelId, let pts): + if boxed { + buffer.appendInt32(277713951) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(channelId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(pts!, buffer: buffer, boxed: false)} + break + case .updateChannelUserTyping(let flags, let channelId, let topMsgId, let fromId, let action): + if boxed { + buffer.appendInt32(-1937192669) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(channelId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} + fromId.serialize(buffer, true) + action.serialize(buffer, true) + break + case .updateChannelViewForumAsMessages(let channelId, let enabled): + if boxed { + buffer.appendInt32(129403168) + } + serializeInt64(channelId, buffer: buffer, boxed: false) + enabled.serialize(buffer, true) + break + case .updateChannelWebPage(let channelId, let webpage, let pts, let ptsCount): + if boxed { + buffer.appendInt32(791390623) + } + serializeInt64(channelId, buffer: buffer, boxed: false) + webpage.serialize(buffer, true) + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + break + case .updateChat(let chatId): + if boxed { + buffer.appendInt32(-124097970) + } + serializeInt64(chatId, buffer: buffer, boxed: false) + break + case .updateChatDefaultBannedRights(let peer, let defaultBannedRights, let version): + if boxed { + buffer.appendInt32(1421875280) + } + peer.serialize(buffer, true) + defaultBannedRights.serialize(buffer, true) + serializeInt32(version, buffer: buffer, boxed: false) + break + case .updateChatParticipant(let flags, let chatId, let date, let actorId, let userId, let prevParticipant, let newParticipant, let invite, let qts): + if boxed { + buffer.appendInt32(-796432838) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(chatId, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt64(actorId, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {prevParticipant!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {newParticipant!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {invite!.serialize(buffer, true)} + serializeInt32(qts, buffer: buffer, boxed: false) + break + case .updateChatParticipantAdd(let chatId, let userId, let inviterId, let date, let version): + if boxed { + buffer.appendInt32(1037718609) + } + serializeInt64(chatId, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + serializeInt64(inviterId, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt32(version, buffer: buffer, boxed: false) + break + case .updateChatParticipantAdmin(let chatId, let userId, let isAdmin, let version): + if boxed { + buffer.appendInt32(-674602590) + } + serializeInt64(chatId, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + isAdmin.serialize(buffer, true) + serializeInt32(version, buffer: buffer, boxed: false) + break + case .updateChatParticipantDelete(let chatId, let userId, let version): + if boxed { + buffer.appendInt32(-483443337) + } + serializeInt64(chatId, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + serializeInt32(version, buffer: buffer, boxed: false) + break + case .updateChatParticipants(let participants): + if boxed { + buffer.appendInt32(125178264) + } + participants.serialize(buffer, true) + break + case .updateChatUserTyping(let chatId, let fromId, let action): + if boxed { + buffer.appendInt32(-2092401936) + } + serializeInt64(chatId, buffer: buffer, boxed: false) + fromId.serialize(buffer, true) + action.serialize(buffer, true) + break + case .updateConfig: + if boxed { + buffer.appendInt32(-1574314746) + } + + break + case .updateContactsReset: + if boxed { + buffer.appendInt32(1887741886) + } + + break + case .updateDcOptions(let dcOptions): + if boxed { + buffer.appendInt32(-1906403213) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(dcOptions.count)) + for item in dcOptions { + item.serialize(buffer, true) + } + break + case .updateDeleteChannelMessages(let channelId, let messages, let pts, let ptsCount): + if boxed { + buffer.appendInt32(-1020437742) + } + serializeInt64(channelId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + serializeInt32(item, buffer: buffer, boxed: false) + } + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + break + case .updateDeleteMessages(let messages, let pts, let ptsCount): + if boxed { + buffer.appendInt32(-1576161051) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + serializeInt32(item, buffer: buffer, boxed: false) + } + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + break + case .updateDeleteScheduledMessages(let peer, let messages): + if boxed { + buffer.appendInt32(-1870238482) + } + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + serializeInt32(item, buffer: buffer, boxed: false) + } + break + case .updateDialogFilter(let flags, let id, let filter): + if boxed { + buffer.appendInt32(654302845) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {filter!.serialize(buffer, true)} + break + case .updateDialogFilterOrder(let order): + if boxed { + buffer.appendInt32(-1512627963) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order.count)) + for item in order { + serializeInt32(item, buffer: buffer, boxed: false) + } + break + case .updateDialogFilters: + if boxed { + buffer.appendInt32(889491791) + } + + break + case .updateDialogPinned(let flags, let folderId, let peer): + if boxed { + buffer.appendInt32(1852826908) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} + peer.serialize(buffer, true) + break + case .updateDialogUnreadMark(let flags, let peer): + if boxed { + buffer.appendInt32(-513517117) + } + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + break + case .updateDraftMessage(let flags, let peer, let topMsgId, let draft): + if boxed { + buffer.appendInt32(457829485) + } + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} + draft.serialize(buffer, true) + break + case .updateEditChannelMessage(let message, let pts, let ptsCount): + if boxed { + buffer.appendInt32(457133559) + } + message.serialize(buffer, true) + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + break + case .updateEditMessage(let message, let pts, let ptsCount): + if boxed { + buffer.appendInt32(-469536605) + } + message.serialize(buffer, true) + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + break + case .updateEncryptedChatTyping(let chatId): + if boxed { + buffer.appendInt32(386986326) + } + serializeInt32(chatId, buffer: buffer, boxed: false) + break + case .updateEncryptedMessagesRead(let chatId, let maxDate, let date): + if boxed { + buffer.appendInt32(956179895) + } + serializeInt32(chatId, buffer: buffer, boxed: false) + serializeInt32(maxDate, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + break + case .updateEncryption(let chat, let date): + if boxed { + buffer.appendInt32(-1264392051) + } + chat.serialize(buffer, true) + serializeInt32(date, buffer: buffer, boxed: false) + break + case .updateFavedStickers: + if boxed { + buffer.appendInt32(-451831443) + } + + break + case .updateFolderPeers(let folderPeers, let pts, let ptsCount): + if boxed { + buffer.appendInt32(422972864) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(folderPeers.count)) + for item in folderPeers { + item.serialize(buffer, true) + } + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + break + case .updateGeoLiveViewed(let peer, let msgId): + if boxed { + buffer.appendInt32(-2027964103) + } + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + break + case .updateGroupCall(let chatId, let call): + if boxed { + buffer.appendInt32(347227392) + } + serializeInt64(chatId, buffer: buffer, boxed: false) + call.serialize(buffer, true) + break + case .updateGroupCallConnection(let flags, let params): + if boxed { + buffer.appendInt32(192428418) + } + serializeInt32(flags, buffer: buffer, boxed: false) + params.serialize(buffer, true) + break + case .updateGroupCallParticipants(let call, let participants, let version): + if boxed { + buffer.appendInt32(-219423922) + } + call.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(participants.count)) + for item in participants { + item.serialize(buffer, true) + } + serializeInt32(version, buffer: buffer, boxed: false) + break + case .updateGroupInvitePrivacyForbidden(let userId): + if boxed { + buffer.appendInt32(-856651050) + } + serializeInt64(userId, buffer: buffer, boxed: false) + break + case .updateInlineBotCallbackQuery(let flags, let queryId, let userId, let msgId, let chatInstance, let data, let gameShortName): + if boxed { + buffer.appendInt32(1763610706) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(queryId, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + msgId.serialize(buffer, true) + serializeInt64(chatInstance, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeBytes(data!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(gameShortName!, buffer: buffer, boxed: false)} + break + case .updateLangPack(let difference): + if boxed { + buffer.appendInt32(1442983757) + } + difference.serialize(buffer, true) + break + case .updateLangPackTooLong(let langCode): + if boxed { + buffer.appendInt32(1180041828) + } + serializeString(langCode, buffer: buffer, boxed: false) + break + case .updateLoginToken: + if boxed { + buffer.appendInt32(1448076945) + } + + break + case .updateMessageExtendedMedia(let peer, let msgId, let extendedMedia): + if boxed { + buffer.appendInt32(1517529484) + } + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + extendedMedia.serialize(buffer, true) + break + case .updateMessageID(let id, let randomId): + if boxed { + buffer.appendInt32(1318109142) + } + serializeInt32(id, buffer: buffer, boxed: false) + serializeInt64(randomId, buffer: buffer, boxed: false) + break + case .updateMessagePoll(let flags, let pollId, let poll, let results): + if boxed { + buffer.appendInt32(-1398708869) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(pollId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {poll!.serialize(buffer, true)} + results.serialize(buffer, true) + break + case .updateMessagePollVote(let pollId, let peer, let options, let qts): + if boxed { + buffer.appendInt32(619974263) + } + serializeInt64(pollId, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(options.count)) + for item in options { + serializeBytes(item, buffer: buffer, boxed: false) + } + serializeInt32(qts, buffer: buffer, boxed: false) + break + case .updateMessageReactions(let flags, let peer, let msgId, let topMsgId, let reactions): + if boxed { + buffer.appendInt32(1578843320) + } + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} + reactions.serialize(buffer, true) + break + case .updateMoveStickerSetToTop(let flags, let stickerset): + if boxed { + buffer.appendInt32(-2030252155) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(stickerset, buffer: buffer, boxed: false) + break + case .updateNewAuthorization(let flags, let hash, let date, let device, let location): + if boxed { + buffer.appendInt32(-1991136273) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(date!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeString(device!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeString(location!, buffer: buffer, boxed: false)} + break + case .updateNewChannelMessage(let message, let pts, let ptsCount): + if boxed { + buffer.appendInt32(1656358105) + } + message.serialize(buffer, true) + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + break + case .updateNewEncryptedMessage(let message, let qts): + if boxed { + buffer.appendInt32(314359194) + } + message.serialize(buffer, true) + serializeInt32(qts, buffer: buffer, boxed: false) + break + case .updateNewMessage(let message, let pts, let ptsCount): + if boxed { + buffer.appendInt32(522914557) + } + message.serialize(buffer, true) + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + break + case .updateNewScheduledMessage(let message): + if boxed { + buffer.appendInt32(967122427) + } + message.serialize(buffer, true) + break + case .updateNewStickerSet(let stickerset): + if boxed { + buffer.appendInt32(1753886890) + } + stickerset.serialize(buffer, true) + break + case .updateNotifySettings(let peer, let notifySettings): + if boxed { + buffer.appendInt32(-1094555409) + } + peer.serialize(buffer, true) + notifySettings.serialize(buffer, true) + break + case .updatePeerBlocked(let flags, let peerId): + if boxed { + buffer.appendInt32(-337610926) + } + serializeInt32(flags, buffer: buffer, boxed: false) + peerId.serialize(buffer, true) + break + case .updatePeerHistoryTTL(let flags, let peer, let ttlPeriod): + if boxed { + buffer.appendInt32(-1147422299) + } + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)} + break + case .updatePeerLocated(let peers): + if boxed { + buffer.appendInt32(-1263546448) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peers.count)) + for item in peers { + item.serialize(buffer, true) + } + break + case .updatePeerSettings(let peer, let settings): + if boxed { + buffer.appendInt32(1786671974) + } + peer.serialize(buffer, true) + settings.serialize(buffer, true) + break + case .updatePeerWallpaper(let flags, let peer, let wallpaper): + if boxed { + buffer.appendInt32(-1371598819) + } + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {wallpaper!.serialize(buffer, true)} + break + case .updatePendingJoinRequests(let peer, let requestsPending, let recentRequesters): + if boxed { + buffer.appendInt32(1885586395) + } + peer.serialize(buffer, true) + serializeInt32(requestsPending, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(recentRequesters.count)) + for item in recentRequesters { + serializeInt64(item, buffer: buffer, boxed: false) + } + break + case .updatePhoneCall(let phoneCall): + if boxed { + buffer.appendInt32(-1425052898) + } + phoneCall.serialize(buffer, true) + break + case .updatePhoneCallSignalingData(let phoneCallId, let data): + if boxed { + buffer.appendInt32(643940105) + } + serializeInt64(phoneCallId, buffer: buffer, boxed: false) + serializeBytes(data, buffer: buffer, boxed: false) + break + case .updatePinnedChannelMessages(let flags, let channelId, let messages, let pts, let ptsCount): + if boxed { + buffer.appendInt32(1538885128) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(channelId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + serializeInt32(item, buffer: buffer, boxed: false) + } + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + break + case .updatePinnedDialogs(let flags, let folderId, let order): + if boxed { + buffer.appendInt32(-99664734) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order!.count)) + for item in order! { + item.serialize(buffer, true) + }} + break + case .updatePinnedMessages(let flags, let peer, let messages, let pts, let ptsCount): + if boxed { + buffer.appendInt32(-309990731) + } + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + serializeInt32(item, buffer: buffer, boxed: false) + } + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + break + case .updatePrivacy(let key, let rules): + if boxed { + buffer.appendInt32(-298113238) + } + key.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(rules.count)) + for item in rules { + item.serialize(buffer, true) + } + break + case .updatePtsChanged: + if boxed { + buffer.appendInt32(861169551) + } + + break + case .updateReadChannelDiscussionInbox(let flags, let channelId, let topMsgId, let readMaxId, let broadcastId, let broadcastPost): + if boxed { + buffer.appendInt32(-693004986) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(channelId, buffer: buffer, boxed: false) + serializeInt32(topMsgId, buffer: buffer, boxed: false) + serializeInt32(readMaxId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt64(broadcastId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(broadcastPost!, buffer: buffer, boxed: false)} + break + case .updateReadChannelDiscussionOutbox(let channelId, let topMsgId, let readMaxId): + if boxed { + buffer.appendInt32(1767677564) + } + serializeInt64(channelId, buffer: buffer, boxed: false) + serializeInt32(topMsgId, buffer: buffer, boxed: false) + serializeInt32(readMaxId, buffer: buffer, boxed: false) + break + case .updateReadChannelInbox(let flags, let folderId, let channelId, let maxId, let stillUnreadCount, let pts): + if boxed { + buffer.appendInt32(-1842450928) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} + serializeInt64(channelId, buffer: buffer, boxed: false) + serializeInt32(maxId, buffer: buffer, boxed: false) + serializeInt32(stillUnreadCount, buffer: buffer, boxed: false) + serializeInt32(pts, buffer: buffer, boxed: false) + break + case .updateReadChannelOutbox(let channelId, let maxId): + if boxed { + buffer.appendInt32(-1218471511) + } + serializeInt64(channelId, buffer: buffer, boxed: false) + serializeInt32(maxId, buffer: buffer, boxed: false) + break + case .updateReadFeaturedEmojiStickers: + if boxed { + buffer.appendInt32(-78886548) + } + + break + case .updateReadFeaturedStickers: + if boxed { + buffer.appendInt32(1461528386) + } + + break + case .updateReadHistoryInbox(let flags, let folderId, let peer, let maxId, let stillUnreadCount, let pts, let ptsCount): + if boxed { + buffer.appendInt32(-1667805217) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} + peer.serialize(buffer, true) + serializeInt32(maxId, buffer: buffer, boxed: false) + serializeInt32(stillUnreadCount, buffer: buffer, boxed: false) + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + break + case .updateReadHistoryOutbox(let peer, let maxId, let pts, let ptsCount): + if boxed { + buffer.appendInt32(791617983) + } + peer.serialize(buffer, true) + serializeInt32(maxId, buffer: buffer, boxed: false) + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + break + case .updateReadMessagesContents(let flags, let messages, let pts, let ptsCount, let date): + if boxed { + buffer.appendInt32(-131960447) + } + serializeInt32(flags, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + serializeInt32(item, buffer: buffer, boxed: false) + } + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(date!, buffer: buffer, boxed: false)} + break + case .updateReadStories(let peer, let maxId): + if boxed { + buffer.appendInt32(-145845461) + } + peer.serialize(buffer, true) + serializeInt32(maxId, buffer: buffer, boxed: false) + break + case .updateRecentEmojiStatuses: + if boxed { + buffer.appendInt32(821314523) + } + + break + case .updateRecentReactions: + if boxed { + buffer.appendInt32(1870160884) + } + + break + case .updateRecentStickers: + if boxed { + buffer.appendInt32(-1706939360) + } + + break + case .updateSavedGifs: + if boxed { + buffer.appendInt32(-1821035490) + } + + break + case .updateSavedRingtones: + if boxed { + buffer.appendInt32(1960361625) + } + + break + case .updateSentStoryReaction(let peer, let storyId, let reaction): + if boxed { + buffer.appendInt32(2103604867) + } + peer.serialize(buffer, true) + serializeInt32(storyId, buffer: buffer, boxed: false) + reaction.serialize(buffer, true) + break + case .updateServiceNotification(let flags, let inboxDate, let type, let message, let media, let entities): + if boxed { + buffer.appendInt32(-337352679) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(inboxDate!, buffer: buffer, boxed: false)} + serializeString(type, buffer: buffer, boxed: false) + serializeString(message, buffer: buffer, boxed: false) + media.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities.count)) + for item in entities { + item.serialize(buffer, true) + } + break + case .updateStickerSets(let flags): + if boxed { + buffer.appendInt32(834816008) + } + serializeInt32(flags, buffer: buffer, boxed: false) + break + case .updateStickerSetsOrder(let flags, let order): + if boxed { + buffer.appendInt32(196268545) + } + serializeInt32(flags, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order.count)) + for item in order { + serializeInt64(item, buffer: buffer, boxed: false) + } + break + case .updateStoriesStealthMode(let stealthMode): + if boxed { + buffer.appendInt32(738741697) + } + stealthMode.serialize(buffer, true) + break + case .updateStory(let peer, let story): + if boxed { + buffer.appendInt32(1974712216) + } + peer.serialize(buffer, true) + story.serialize(buffer, true) + break + case .updateStoryID(let id, let randomId): + if boxed { + buffer.appendInt32(468923833) + } + serializeInt32(id, buffer: buffer, boxed: false) + serializeInt64(randomId, buffer: buffer, boxed: false) + break + case .updateTheme(let theme): + if boxed { + buffer.appendInt32(-2112423005) + } + theme.serialize(buffer, true) + break + case .updateTranscribedAudio(let flags, let peer, let msgId, let transcriptionId, let text): + if boxed { + buffer.appendInt32(8703322) + } + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + serializeInt64(transcriptionId, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) + break + case .updateUser(let userId): + if boxed { + buffer.appendInt32(542282808) + } + serializeInt64(userId, buffer: buffer, boxed: false) + break + case .updateUserEmojiStatus(let userId, let emojiStatus): + if boxed { + buffer.appendInt32(674706841) + } + serializeInt64(userId, buffer: buffer, boxed: false) + emojiStatus.serialize(buffer, true) + break + case .updateUserName(let userId, let firstName, let lastName, let usernames): + if boxed { + buffer.appendInt32(-1484486364) + } + serializeInt64(userId, buffer: buffer, boxed: false) + serializeString(firstName, buffer: buffer, boxed: false) + serializeString(lastName, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(usernames.count)) + for item in usernames { + item.serialize(buffer, true) + } + break + case .updateUserPhone(let userId, let phone): + if boxed { + buffer.appendInt32(88680979) + } + serializeInt64(userId, buffer: buffer, boxed: false) + serializeString(phone, buffer: buffer, boxed: false) + break + case .updateUserStatus(let userId, let status): + if boxed { + buffer.appendInt32(-440534818) + } + serializeInt64(userId, buffer: buffer, boxed: false) + status.serialize(buffer, true) + break + case .updateUserTyping(let userId, let action): + if boxed { + buffer.appendInt32(-1071741569) + } + serializeInt64(userId, buffer: buffer, boxed: false) + action.serialize(buffer, true) + break + case .updateWebPage(let webpage, let pts, let ptsCount): + if boxed { + buffer.appendInt32(2139689491) + } + webpage.serialize(buffer, true) + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + break + case .updateWebViewResultSent(let queryId): + if boxed { + buffer.appendInt32(361936797) + } + serializeInt64(queryId, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .updateAttachMenuBots: + return ("updateAttachMenuBots", []) + case .updateAutoSaveSettings: + return ("updateAutoSaveSettings", []) + case .updateBotCallbackQuery(let flags, let queryId, let userId, let peer, let msgId, let chatInstance, let data, let gameShortName): + return ("updateBotCallbackQuery", [("flags", flags as Any), ("queryId", queryId as Any), ("userId", userId as Any), ("peer", peer as Any), ("msgId", msgId as Any), ("chatInstance", chatInstance as Any), ("data", data as Any), ("gameShortName", gameShortName as Any)]) + case .updateBotChatBoost(let peer, let boost, let qts): + return ("updateBotChatBoost", [("peer", peer as Any), ("boost", boost as Any), ("qts", qts as Any)]) + case .updateBotChatInviteRequester(let peer, let date, let userId, let about, let invite, let qts): + return ("updateBotChatInviteRequester", [("peer", peer as Any), ("date", date as Any), ("userId", userId as Any), ("about", about as Any), ("invite", invite as Any), ("qts", qts as Any)]) + case .updateBotCommands(let peer, let botId, let commands): + return ("updateBotCommands", [("peer", peer as Any), ("botId", botId as Any), ("commands", commands as Any)]) + case .updateBotInlineQuery(let flags, let queryId, let userId, let query, let geo, let peerType, let offset): + return ("updateBotInlineQuery", [("flags", flags as Any), ("queryId", queryId as Any), ("userId", userId as Any), ("query", query as Any), ("geo", geo as Any), ("peerType", peerType as Any), ("offset", offset as Any)]) + case .updateBotInlineSend(let flags, let userId, let query, let geo, let id, let msgId): + return ("updateBotInlineSend", [("flags", flags as Any), ("userId", userId as Any), ("query", query as Any), ("geo", geo as Any), ("id", id as Any), ("msgId", msgId as Any)]) + case .updateBotMenuButton(let botId, let button): + return ("updateBotMenuButton", [("botId", botId as Any), ("button", button as Any)]) + case .updateBotPrecheckoutQuery(let flags, let queryId, let userId, let payload, let info, let shippingOptionId, let currency, let totalAmount): + return ("updateBotPrecheckoutQuery", [("flags", flags as Any), ("queryId", queryId as Any), ("userId", userId as Any), ("payload", payload as Any), ("info", info as Any), ("shippingOptionId", shippingOptionId as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any)]) + case .updateBotShippingQuery(let queryId, let userId, let payload, let shippingAddress): + return ("updateBotShippingQuery", [("queryId", queryId as Any), ("userId", userId as Any), ("payload", payload as Any), ("shippingAddress", shippingAddress as Any)]) + case .updateBotStopped(let userId, let date, let stopped, let qts): + return ("updateBotStopped", [("userId", userId as Any), ("date", date as Any), ("stopped", stopped as Any), ("qts", qts as Any)]) + case .updateBotWebhookJSON(let data): + return ("updateBotWebhookJSON", [("data", data as Any)]) + case .updateBotWebhookJSONQuery(let queryId, let data, let timeout): + return ("updateBotWebhookJSONQuery", [("queryId", queryId as Any), ("data", data as Any), ("timeout", timeout as Any)]) + case .updateChannel(let channelId): + return ("updateChannel", [("channelId", channelId as Any)]) + case .updateChannelAvailableMessages(let channelId, let availableMinId): + return ("updateChannelAvailableMessages", [("channelId", channelId as Any), ("availableMinId", availableMinId as Any)]) + case .updateChannelMessageForwards(let channelId, let id, let forwards): + return ("updateChannelMessageForwards", [("channelId", channelId as Any), ("id", id as Any), ("forwards", forwards as Any)]) + case .updateChannelMessageViews(let channelId, let id, let views): + return ("updateChannelMessageViews", [("channelId", channelId as Any), ("id", id as Any), ("views", views as Any)]) + case .updateChannelParticipant(let flags, let channelId, let date, let actorId, let userId, let prevParticipant, let newParticipant, let invite, let qts): + return ("updateChannelParticipant", [("flags", flags as Any), ("channelId", channelId as Any), ("date", date as Any), ("actorId", actorId as Any), ("userId", userId as Any), ("prevParticipant", prevParticipant as Any), ("newParticipant", newParticipant as Any), ("invite", invite as Any), ("qts", qts as Any)]) + case .updateChannelPinnedTopic(let flags, let channelId, let topicId): + return ("updateChannelPinnedTopic", [("flags", flags as Any), ("channelId", channelId as Any), ("topicId", topicId as Any)]) + case .updateChannelPinnedTopics(let flags, let channelId, let order): + return ("updateChannelPinnedTopics", [("flags", flags as Any), ("channelId", channelId as Any), ("order", order as Any)]) + case .updateChannelReadMessagesContents(let flags, let channelId, let topMsgId, let messages): + return ("updateChannelReadMessagesContents", [("flags", flags as Any), ("channelId", channelId as Any), ("topMsgId", topMsgId as Any), ("messages", messages as Any)]) + case .updateChannelTooLong(let flags, let channelId, let pts): + return ("updateChannelTooLong", [("flags", flags as Any), ("channelId", channelId as Any), ("pts", pts as Any)]) + case .updateChannelUserTyping(let flags, let channelId, let topMsgId, let fromId, let action): + return ("updateChannelUserTyping", [("flags", flags as Any), ("channelId", channelId as Any), ("topMsgId", topMsgId as Any), ("fromId", fromId as Any), ("action", action as Any)]) + case .updateChannelViewForumAsMessages(let channelId, let enabled): + return ("updateChannelViewForumAsMessages", [("channelId", channelId as Any), ("enabled", enabled as Any)]) + case .updateChannelWebPage(let channelId, let webpage, let pts, let ptsCount): + return ("updateChannelWebPage", [("channelId", channelId as Any), ("webpage", webpage as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) + case .updateChat(let chatId): + return ("updateChat", [("chatId", chatId as Any)]) + case .updateChatDefaultBannedRights(let peer, let defaultBannedRights, let version): + return ("updateChatDefaultBannedRights", [("peer", peer as Any), ("defaultBannedRights", defaultBannedRights as Any), ("version", version as Any)]) + case .updateChatParticipant(let flags, let chatId, let date, let actorId, let userId, let prevParticipant, let newParticipant, let invite, let qts): + return ("updateChatParticipant", [("flags", flags as Any), ("chatId", chatId as Any), ("date", date as Any), ("actorId", actorId as Any), ("userId", userId as Any), ("prevParticipant", prevParticipant as Any), ("newParticipant", newParticipant as Any), ("invite", invite as Any), ("qts", qts as Any)]) + case .updateChatParticipantAdd(let chatId, let userId, let inviterId, let date, let version): + return ("updateChatParticipantAdd", [("chatId", chatId as Any), ("userId", userId as Any), ("inviterId", inviterId as Any), ("date", date as Any), ("version", version as Any)]) + case .updateChatParticipantAdmin(let chatId, let userId, let isAdmin, let version): + return ("updateChatParticipantAdmin", [("chatId", chatId as Any), ("userId", userId as Any), ("isAdmin", isAdmin as Any), ("version", version as Any)]) + case .updateChatParticipantDelete(let chatId, let userId, let version): + return ("updateChatParticipantDelete", [("chatId", chatId as Any), ("userId", userId as Any), ("version", version as Any)]) + case .updateChatParticipants(let participants): + return ("updateChatParticipants", [("participants", participants as Any)]) + case .updateChatUserTyping(let chatId, let fromId, let action): + return ("updateChatUserTyping", [("chatId", chatId as Any), ("fromId", fromId as Any), ("action", action as Any)]) + case .updateConfig: + return ("updateConfig", []) + case .updateContactsReset: + return ("updateContactsReset", []) + case .updateDcOptions(let dcOptions): + return ("updateDcOptions", [("dcOptions", dcOptions as Any)]) + case .updateDeleteChannelMessages(let channelId, let messages, let pts, let ptsCount): + return ("updateDeleteChannelMessages", [("channelId", channelId as Any), ("messages", messages as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) + case .updateDeleteMessages(let messages, let pts, let ptsCount): + return ("updateDeleteMessages", [("messages", messages as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) + case .updateDeleteScheduledMessages(let peer, let messages): + return ("updateDeleteScheduledMessages", [("peer", peer as Any), ("messages", messages as Any)]) + case .updateDialogFilter(let flags, let id, let filter): + return ("updateDialogFilter", [("flags", flags as Any), ("id", id as Any), ("filter", filter as Any)]) + case .updateDialogFilterOrder(let order): + return ("updateDialogFilterOrder", [("order", order as Any)]) + case .updateDialogFilters: + return ("updateDialogFilters", []) + case .updateDialogPinned(let flags, let folderId, let peer): + return ("updateDialogPinned", [("flags", flags as Any), ("folderId", folderId as Any), ("peer", peer as Any)]) + case .updateDialogUnreadMark(let flags, let peer): + return ("updateDialogUnreadMark", [("flags", flags as Any), ("peer", peer as Any)]) + case .updateDraftMessage(let flags, let peer, let topMsgId, let draft): + return ("updateDraftMessage", [("flags", flags as Any), ("peer", peer as Any), ("topMsgId", topMsgId as Any), ("draft", draft as Any)]) + case .updateEditChannelMessage(let message, let pts, let ptsCount): + return ("updateEditChannelMessage", [("message", message as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) + case .updateEditMessage(let message, let pts, let ptsCount): + return ("updateEditMessage", [("message", message as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) + case .updateEncryptedChatTyping(let chatId): + return ("updateEncryptedChatTyping", [("chatId", chatId as Any)]) + case .updateEncryptedMessagesRead(let chatId, let maxDate, let date): + return ("updateEncryptedMessagesRead", [("chatId", chatId as Any), ("maxDate", maxDate as Any), ("date", date as Any)]) + case .updateEncryption(let chat, let date): + return ("updateEncryption", [("chat", chat as Any), ("date", date as Any)]) + case .updateFavedStickers: + return ("updateFavedStickers", []) + case .updateFolderPeers(let folderPeers, let pts, let ptsCount): + return ("updateFolderPeers", [("folderPeers", folderPeers as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) + case .updateGeoLiveViewed(let peer, let msgId): + return ("updateGeoLiveViewed", [("peer", peer as Any), ("msgId", msgId as Any)]) + case .updateGroupCall(let chatId, let call): + return ("updateGroupCall", [("chatId", chatId as Any), ("call", call as Any)]) + case .updateGroupCallConnection(let flags, let params): + return ("updateGroupCallConnection", [("flags", flags as Any), ("params", params as Any)]) + case .updateGroupCallParticipants(let call, let participants, let version): + return ("updateGroupCallParticipants", [("call", call as Any), ("participants", participants as Any), ("version", version as Any)]) + case .updateGroupInvitePrivacyForbidden(let userId): + return ("updateGroupInvitePrivacyForbidden", [("userId", userId as Any)]) + case .updateInlineBotCallbackQuery(let flags, let queryId, let userId, let msgId, let chatInstance, let data, let gameShortName): + return ("updateInlineBotCallbackQuery", [("flags", flags as Any), ("queryId", queryId as Any), ("userId", userId as Any), ("msgId", msgId as Any), ("chatInstance", chatInstance as Any), ("data", data as Any), ("gameShortName", gameShortName as Any)]) + case .updateLangPack(let difference): + return ("updateLangPack", [("difference", difference as Any)]) + case .updateLangPackTooLong(let langCode): + return ("updateLangPackTooLong", [("langCode", langCode as Any)]) + case .updateLoginToken: + return ("updateLoginToken", []) + case .updateMessageExtendedMedia(let peer, let msgId, let extendedMedia): + return ("updateMessageExtendedMedia", [("peer", peer as Any), ("msgId", msgId as Any), ("extendedMedia", extendedMedia as Any)]) + case .updateMessageID(let id, let randomId): + return ("updateMessageID", [("id", id as Any), ("randomId", randomId as Any)]) + case .updateMessagePoll(let flags, let pollId, let poll, let results): + return ("updateMessagePoll", [("flags", flags as Any), ("pollId", pollId as Any), ("poll", poll as Any), ("results", results as Any)]) + case .updateMessagePollVote(let pollId, let peer, let options, let qts): + return ("updateMessagePollVote", [("pollId", pollId as Any), ("peer", peer as Any), ("options", options as Any), ("qts", qts as Any)]) + case .updateMessageReactions(let flags, let peer, let msgId, let topMsgId, let reactions): + return ("updateMessageReactions", [("flags", flags as Any), ("peer", peer as Any), ("msgId", msgId as Any), ("topMsgId", topMsgId as Any), ("reactions", reactions as Any)]) + case .updateMoveStickerSetToTop(let flags, let stickerset): + return ("updateMoveStickerSetToTop", [("flags", flags as Any), ("stickerset", stickerset as Any)]) + case .updateNewAuthorization(let flags, let hash, let date, let device, let location): + return ("updateNewAuthorization", [("flags", flags as Any), ("hash", hash as Any), ("date", date as Any), ("device", device as Any), ("location", location as Any)]) + case .updateNewChannelMessage(let message, let pts, let ptsCount): + return ("updateNewChannelMessage", [("message", message as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) + case .updateNewEncryptedMessage(let message, let qts): + return ("updateNewEncryptedMessage", [("message", message as Any), ("qts", qts as Any)]) + case .updateNewMessage(let message, let pts, let ptsCount): + return ("updateNewMessage", [("message", message as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) + case .updateNewScheduledMessage(let message): + return ("updateNewScheduledMessage", [("message", message as Any)]) + case .updateNewStickerSet(let stickerset): + return ("updateNewStickerSet", [("stickerset", stickerset as Any)]) + case .updateNotifySettings(let peer, let notifySettings): + return ("updateNotifySettings", [("peer", peer as Any), ("notifySettings", notifySettings as Any)]) + case .updatePeerBlocked(let flags, let peerId): + return ("updatePeerBlocked", [("flags", flags as Any), ("peerId", peerId as Any)]) + case .updatePeerHistoryTTL(let flags, let peer, let ttlPeriod): + return ("updatePeerHistoryTTL", [("flags", flags as Any), ("peer", peer as Any), ("ttlPeriod", ttlPeriod as Any)]) + case .updatePeerLocated(let peers): + return ("updatePeerLocated", [("peers", peers as Any)]) + case .updatePeerSettings(let peer, let settings): + return ("updatePeerSettings", [("peer", peer as Any), ("settings", settings as Any)]) + case .updatePeerWallpaper(let flags, let peer, let wallpaper): + return ("updatePeerWallpaper", [("flags", flags as Any), ("peer", peer as Any), ("wallpaper", wallpaper as Any)]) + case .updatePendingJoinRequests(let peer, let requestsPending, let recentRequesters): + return ("updatePendingJoinRequests", [("peer", peer as Any), ("requestsPending", requestsPending as Any), ("recentRequesters", recentRequesters as Any)]) + case .updatePhoneCall(let phoneCall): + return ("updatePhoneCall", [("phoneCall", phoneCall as Any)]) + case .updatePhoneCallSignalingData(let phoneCallId, let data): + return ("updatePhoneCallSignalingData", [("phoneCallId", phoneCallId as Any), ("data", data as Any)]) + case .updatePinnedChannelMessages(let flags, let channelId, let messages, let pts, let ptsCount): + return ("updatePinnedChannelMessages", [("flags", flags as Any), ("channelId", channelId as Any), ("messages", messages as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) + case .updatePinnedDialogs(let flags, let folderId, let order): + return ("updatePinnedDialogs", [("flags", flags as Any), ("folderId", folderId as Any), ("order", order as Any)]) + case .updatePinnedMessages(let flags, let peer, let messages, let pts, let ptsCount): + return ("updatePinnedMessages", [("flags", flags as Any), ("peer", peer as Any), ("messages", messages as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) + case .updatePrivacy(let key, let rules): + return ("updatePrivacy", [("key", key as Any), ("rules", rules as Any)]) + case .updatePtsChanged: + return ("updatePtsChanged", []) + case .updateReadChannelDiscussionInbox(let flags, let channelId, let topMsgId, let readMaxId, let broadcastId, let broadcastPost): + return ("updateReadChannelDiscussionInbox", [("flags", flags as Any), ("channelId", channelId as Any), ("topMsgId", topMsgId as Any), ("readMaxId", readMaxId as Any), ("broadcastId", broadcastId as Any), ("broadcastPost", broadcastPost as Any)]) + case .updateReadChannelDiscussionOutbox(let channelId, let topMsgId, let readMaxId): + return ("updateReadChannelDiscussionOutbox", [("channelId", channelId as Any), ("topMsgId", topMsgId as Any), ("readMaxId", readMaxId as Any)]) + case .updateReadChannelInbox(let flags, let folderId, let channelId, let maxId, let stillUnreadCount, let pts): + return ("updateReadChannelInbox", [("flags", flags as Any), ("folderId", folderId as Any), ("channelId", channelId as Any), ("maxId", maxId as Any), ("stillUnreadCount", stillUnreadCount as Any), ("pts", pts as Any)]) + case .updateReadChannelOutbox(let channelId, let maxId): + return ("updateReadChannelOutbox", [("channelId", channelId as Any), ("maxId", maxId as Any)]) + case .updateReadFeaturedEmojiStickers: + return ("updateReadFeaturedEmojiStickers", []) + case .updateReadFeaturedStickers: + return ("updateReadFeaturedStickers", []) + case .updateReadHistoryInbox(let flags, let folderId, let peer, let maxId, let stillUnreadCount, let pts, let ptsCount): + return ("updateReadHistoryInbox", [("flags", flags as Any), ("folderId", folderId as Any), ("peer", peer as Any), ("maxId", maxId as Any), ("stillUnreadCount", stillUnreadCount as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) + case .updateReadHistoryOutbox(let peer, let maxId, let pts, let ptsCount): + return ("updateReadHistoryOutbox", [("peer", peer as Any), ("maxId", maxId as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) + case .updateReadMessagesContents(let flags, let messages, let pts, let ptsCount, let date): + return ("updateReadMessagesContents", [("flags", flags as Any), ("messages", messages as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any), ("date", date as Any)]) + case .updateReadStories(let peer, let maxId): + return ("updateReadStories", [("peer", peer as Any), ("maxId", maxId as Any)]) + case .updateRecentEmojiStatuses: + return ("updateRecentEmojiStatuses", []) + case .updateRecentReactions: + return ("updateRecentReactions", []) + case .updateRecentStickers: + return ("updateRecentStickers", []) + case .updateSavedGifs: + return ("updateSavedGifs", []) + case .updateSavedRingtones: + return ("updateSavedRingtones", []) + case .updateSentStoryReaction(let peer, let storyId, let reaction): + return ("updateSentStoryReaction", [("peer", peer as Any), ("storyId", storyId as Any), ("reaction", reaction as Any)]) + case .updateServiceNotification(let flags, let inboxDate, let type, let message, let media, let entities): + return ("updateServiceNotification", [("flags", flags as Any), ("inboxDate", inboxDate as Any), ("type", type as Any), ("message", message as Any), ("media", media as Any), ("entities", entities as Any)]) + case .updateStickerSets(let flags): + return ("updateStickerSets", [("flags", flags as Any)]) + case .updateStickerSetsOrder(let flags, let order): + return ("updateStickerSetsOrder", [("flags", flags as Any), ("order", order as Any)]) + case .updateStoriesStealthMode(let stealthMode): + return ("updateStoriesStealthMode", [("stealthMode", stealthMode as Any)]) + case .updateStory(let peer, let story): + return ("updateStory", [("peer", peer as Any), ("story", story as Any)]) + case .updateStoryID(let id, let randomId): + return ("updateStoryID", [("id", id as Any), ("randomId", randomId as Any)]) + case .updateTheme(let theme): + return ("updateTheme", [("theme", theme as Any)]) + case .updateTranscribedAudio(let flags, let peer, let msgId, let transcriptionId, let text): + return ("updateTranscribedAudio", [("flags", flags as Any), ("peer", peer as Any), ("msgId", msgId as Any), ("transcriptionId", transcriptionId as Any), ("text", text as Any)]) + case .updateUser(let userId): + return ("updateUser", [("userId", userId as Any)]) + case .updateUserEmojiStatus(let userId, let emojiStatus): + return ("updateUserEmojiStatus", [("userId", userId as Any), ("emojiStatus", emojiStatus as Any)]) + case .updateUserName(let userId, let firstName, let lastName, let usernames): + return ("updateUserName", [("userId", userId as Any), ("firstName", firstName as Any), ("lastName", lastName as Any), ("usernames", usernames as Any)]) + case .updateUserPhone(let userId, let phone): + return ("updateUserPhone", [("userId", userId as Any), ("phone", phone as Any)]) + case .updateUserStatus(let userId, let status): + return ("updateUserStatus", [("userId", userId as Any), ("status", status as Any)]) + case .updateUserTyping(let userId, let action): + return ("updateUserTyping", [("userId", userId as Any), ("action", action as Any)]) + case .updateWebPage(let webpage, let pts, let ptsCount): + return ("updateWebPage", [("webpage", webpage as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) + case .updateWebViewResultSent(let queryId): + return ("updateWebViewResultSent", [("queryId", queryId as Any)]) + } + } + + public static func parse_updateAttachMenuBots(_ reader: BufferReader) -> Update? { + return Api.Update.updateAttachMenuBots + } + public static func parse_updateAutoSaveSettings(_ reader: BufferReader) -> Update? { + return Api.Update.updateAutoSaveSettings + } + public static func parse_updateBotCallbackQuery(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int64? + _3 = reader.readInt64() + var _4: Api.Peer? + if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _5: Int32? + _5 = reader.readInt32() + var _6: Int64? + _6 = reader.readInt64() + var _7: Buffer? + if Int(_1!) & Int(1 << 0) != 0 {_7 = parseBytes(reader) } + var _8: String? + if Int(_1!) & Int(1 << 1) != 0 {_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 << 0) == 0) || _7 != nil + let _c8 = (Int(_1!) & Int(1 << 1) == 0) || _8 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { + return Api.Update.updateBotCallbackQuery(flags: _1!, queryId: _2!, userId: _3!, peer: _4!, msgId: _5!, chatInstance: _6!, data: _7, gameShortName: _8) + } + else { + return nil + } + } + public static func parse_updateBotChatBoost(_ reader: BufferReader) -> Update? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _2: Api.Boost? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Boost + } + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updateBotChatBoost(peer: _1!, boost: _2!, qts: _3!) + } + else { + return nil + } + } + public static func parse_updateBotChatInviteRequester(_ reader: BufferReader) -> Update? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _2: Int32? + _2 = reader.readInt32() + var _3: Int64? + _3 = reader.readInt64() + var _4: String? + _4 = parseString(reader) + var _5: Api.ExportedChatInvite? + if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite + } + var _6: Int32? + _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 = _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.Update.updateBotChatInviteRequester(peer: _1!, date: _2!, userId: _3!, about: _4!, invite: _5!, qts: _6!) + } + else { + return nil + } + } + public static func parse_updateBotCommands(_ reader: BufferReader) -> Update? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _2: Int64? + _2 = reader.readInt64() + var _3: [Api.BotCommand]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.BotCommand.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updateBotCommands(peer: _1!, botId: _2!, commands: _3!) + } + else { + return nil + } + } + public static func parse_updateBotInlineQuery(_ reader: BufferReader) -> Update? { + 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: Api.GeoPoint? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.GeoPoint + } } + var _6: Api.InlineQueryPeerType? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.InlineQueryPeerType + } } + var _7: String? + _7 = parseString(reader) + 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 << 1) == 0) || _6 != nil + let _c7 = _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.Update.updateBotInlineQuery(flags: _1!, queryId: _2!, userId: _3!, query: _4!, geo: _5, peerType: _6, offset: _7!) + } + else { + return nil + } + } + public static func parse_updateBotInlineSend(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: String? + _3 = parseString(reader) + var _4: Api.GeoPoint? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.GeoPoint + } } + var _5: String? + _5 = parseString(reader) + var _6: Api.InputBotInlineMessageID? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.InputBotInlineMessageID + } } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c5 = _5 != nil + let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.Update.updateBotInlineSend(flags: _1!, userId: _2!, query: _3!, geo: _4, id: _5!, msgId: _6) + } + else { + return nil + } + } + public static func parse_updateBotMenuButton(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Api.BotMenuButton? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.BotMenuButton + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updateBotMenuButton(botId: _1!, button: _2!) + } + else { + return nil + } + } + public static func parse_updateBotPrecheckoutQuery(_ reader: BufferReader) -> Update? { + 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: Api.PaymentRequestedInfo? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo + } } + var _6: String? + if Int(_1!) & Int(1 << 1) != 0 {_6 = parseString(reader) } + var _7: String? + _7 = parseString(reader) + var _8: Int64? + _8 = reader.readInt64() + 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 << 1) == 0) || _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { + return Api.Update.updateBotPrecheckoutQuery(flags: _1!, queryId: _2!, userId: _3!, payload: _4!, info: _5, shippingOptionId: _6, currency: _7!, totalAmount: _8!) + } + else { + return nil + } + } + public static func parse_updateBotShippingQuery(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int64? + _2 = reader.readInt64() + var _3: Buffer? + _3 = parseBytes(reader) + var _4: Api.PostAddress? + if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.PostAddress + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.Update.updateBotShippingQuery(queryId: _1!, userId: _2!, payload: _3!, shippingAddress: _4!) + } + else { + return nil + } + } + public static func parse_updateBotStopped(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int32? + _2 = reader.readInt32() + var _3: Api.Bool? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.Bool + } + 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.Update.updateBotStopped(userId: _1!, date: _2!, stopped: _3!, qts: _4!) + } + else { + return nil + } + } + public static func parse_updateBotWebhookJSON(_ reader: BufferReader) -> Update? { + var _1: Api.DataJSON? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.DataJSON + } + let _c1 = _1 != nil + if _c1 { + return Api.Update.updateBotWebhookJSON(data: _1!) + } + else { + return nil + } + } + public static func parse_updateBotWebhookJSONQuery(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Api.DataJSON? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.DataJSON + } + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updateBotWebhookJSONQuery(queryId: _1!, data: _2!, timeout: _3!) + } + else { + return nil + } + } + public static func parse_updateChannel(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + let _c1 = _1 != nil + if _c1 { + return Api.Update.updateChannel(channelId: _1!) + } + else { + return nil + } + } + public static func parse_updateChannelAvailableMessages(_ reader: BufferReader) -> Update? { + 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.Update.updateChannelAvailableMessages(channelId: _1!, availableMinId: _2!) + } + else { + return nil + } + } + public static func parse_updateChannelMessageForwards(_ reader: BufferReader) -> Update? { + 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.Update.updateChannelMessageForwards(channelId: _1!, id: _2!, forwards: _3!) + } + else { + return nil + } + } + public static func parse_updateChannelMessageViews(_ reader: BufferReader) -> Update? { + 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.Update.updateChannelMessageViews(channelId: _1!, id: _2!, views: _3!) + } + else { + return nil + } + } + public static func parse_updateChannelParticipant(_ reader: BufferReader) -> Update? { + 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: Api.ChannelParticipant? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.ChannelParticipant + } } + var _7: Api.ChannelParticipant? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.ChannelParticipant + } } + var _8: Api.ExportedChatInvite? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite + } } + var _9: Int32? + _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 << 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 + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return Api.Update.updateChannelParticipant(flags: _1!, channelId: _2!, date: _3!, actorId: _4!, userId: _5!, prevParticipant: _6, newParticipant: _7, invite: _8, qts: _9!) + } + else { + return nil + } + } + public static func parse_updateChannelPinnedTopic(_ reader: BufferReader) -> Update? { + 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.Update.updateChannelPinnedTopic(flags: _1!, channelId: _2!, topicId: _3!) + } + else { + return nil + } + } + public static func parse_updateChannelPinnedTopics(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: [Int32]? + if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updateChannelPinnedTopics(flags: _1!, channelId: _2!, order: _3) + } + else { + return nil + } + } + public static func parse_updateChannelReadMessagesContents(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } + var _4: [Int32]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.Update.updateChannelReadMessagesContents(flags: _1!, channelId: _2!, topMsgId: _3, messages: _4!) + } + else { + return nil + } + } + public static func parse_updateChannelTooLong(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updateChannelTooLong(flags: _1!, channelId: _2!, pts: _3) + } + else { + return nil + } + } + public static func parse_updateChannelUserTyping(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } + var _4: Api.Peer? + if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _5: Api.SendMessageAction? + if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.SendMessageAction + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.Update.updateChannelUserTyping(flags: _1!, channelId: _2!, topMsgId: _3, fromId: _4!, action: _5!) + } + else { + return nil + } + } + public static func parse_updateChannelViewForumAsMessages(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Api.Bool? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Bool + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updateChannelViewForumAsMessages(channelId: _1!, enabled: _2!) + } + else { + return nil + } + } + public static func parse_updateChannelWebPage(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Api.WebPage? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.WebPage + } + 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.Update.updateChannelWebPage(channelId: _1!, webpage: _2!, pts: _3!, ptsCount: _4!) + } + else { + return nil + } + } + public static func parse_updateChat(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + let _c1 = _1 != nil + if _c1 { + return Api.Update.updateChat(chatId: _1!) + } + else { + return nil + } + } + public static func parse_updateChatDefaultBannedRights(_ reader: BufferReader) -> Update? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _2: Api.ChatBannedRights? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.ChatBannedRights + } + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updateChatDefaultBannedRights(peer: _1!, defaultBannedRights: _2!, version: _3!) + } + else { + return nil + } + } + public static func parse_updateChatParticipant(_ reader: BufferReader) -> Update? { + 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: Api.ChatParticipant? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.ChatParticipant + } } + var _7: Api.ChatParticipant? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.ChatParticipant + } } + var _8: Api.ExportedChatInvite? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite + } } + var _9: Int32? + _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 << 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 + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return Api.Update.updateChatParticipant(flags: _1!, chatId: _2!, date: _3!, actorId: _4!, userId: _5!, prevParticipant: _6, newParticipant: _7, invite: _8, qts: _9!) + } + else { + return nil + } + } + public static func parse_updateChatParticipantAdd(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int64? + _3 = reader.readInt64() + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() + 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.Update.updateChatParticipantAdd(chatId: _1!, userId: _2!, inviterId: _3!, date: _4!, version: _5!) + } + else { + return nil + } + } + public static func parse_updateChatParticipantAdmin(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int64? + _2 = reader.readInt64() + var _3: Api.Bool? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.Bool + } + 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.Update.updateChatParticipantAdmin(chatId: _1!, userId: _2!, isAdmin: _3!, version: _4!) + } + else { + return nil + } + } + public static func parse_updateChatParticipantDelete(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + 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.Update.updateChatParticipantDelete(chatId: _1!, userId: _2!, version: _3!) + } + else { + return nil + } + } + public static func parse_updateChatParticipants(_ reader: BufferReader) -> Update? { + var _1: Api.ChatParticipants? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.ChatParticipants + } + let _c1 = _1 != nil + if _c1 { + return Api.Update.updateChatParticipants(participants: _1!) + } + else { + return nil + } + } + public static func parse_updateChatUserTyping(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Api.Peer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _3: Api.SendMessageAction? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.SendMessageAction + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updateChatUserTyping(chatId: _1!, fromId: _2!, action: _3!) + } + else { + return nil + } + } + public static func parse_updateConfig(_ reader: BufferReader) -> Update? { + return Api.Update.updateConfig + } + public static func parse_updateContactsReset(_ reader: BufferReader) -> Update? { + return Api.Update.updateContactsReset + } + public static func parse_updateDcOptions(_ reader: BufferReader) -> Update? { + var _1: [Api.DcOption]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DcOption.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.Update.updateDcOptions(dcOptions: _1!) + } + else { + return nil + } + } + public static func parse_updateDeleteChannelMessages(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Int32]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + 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.Update.updateDeleteChannelMessages(channelId: _1!, messages: _2!, pts: _3!, ptsCount: _4!) + } + else { + return nil + } + } + public static func parse_updateDeleteMessages(_ reader: BufferReader) -> Update? { + var _1: [Int32]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + 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.Update.updateDeleteMessages(messages: _1!, pts: _2!, ptsCount: _3!) + } + else { + return nil + } + } + public static func parse_updateDeleteScheduledMessages(_ reader: BufferReader) -> Update? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + 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.Update.updateDeleteScheduledMessages(peer: _1!, messages: _2!) + } + else { + return nil + } + } + public static func parse_updateDialogFilter(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Api.DialogFilter? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.DialogFilter + } } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updateDialogFilter(flags: _1!, id: _2!, filter: _3) + } + else { + return nil + } + } + public static func parse_updateDialogFilterOrder(_ reader: BufferReader) -> Update? { + var _1: [Int32]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.Update.updateDialogFilterOrder(order: _1!) + } + else { + return nil + } + } + public static func parse_updateDialogFilters(_ reader: BufferReader) -> Update? { + return Api.Update.updateDialogFilters + } + public static func parse_updateDialogPinned(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() } + var _3: Api.DialogPeer? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.DialogPeer + } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updateDialogPinned(flags: _1!, folderId: _2, peer: _3!) + } + else { + return nil + } + } + public static func parse_updateDialogUnreadMark(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.DialogPeer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.DialogPeer + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updateDialogUnreadMark(flags: _1!, peer: _2!) + } + else { + return nil + } + } + public static func parse_updateDraftMessage(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Peer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _3: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } + var _4: Api.DraftMessage? + if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.DraftMessage + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.Update.updateDraftMessage(flags: _1!, peer: _2!, topMsgId: _3, draft: _4!) + } + else { + return nil + } + } + public static func parse_updateEditChannelMessage(_ reader: BufferReader) -> Update? { + var _1: Api.Message? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Message + } + 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.Update.updateEditChannelMessage(message: _1!, pts: _2!, ptsCount: _3!) + } + else { + return nil + } + } + public static func parse_updateEditMessage(_ reader: BufferReader) -> Update? { + var _1: Api.Message? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Message + } + 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.Update.updateEditMessage(message: _1!, pts: _2!, ptsCount: _3!) + } + else { + return nil + } + } + public static func parse_updateEncryptedChatTyping(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.Update.updateEncryptedChatTyping(chatId: _1!) + } + else { + return nil + } + } + public static func parse_updateEncryptedMessagesRead(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + 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.Update.updateEncryptedMessagesRead(chatId: _1!, maxDate: _2!, date: _3!) + } + else { + return nil + } + } + public static func parse_updateEncryption(_ reader: BufferReader) -> Update? { + var _1: Api.EncryptedChat? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.EncryptedChat + } + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updateEncryption(chat: _1!, date: _2!) + } + else { + return nil + } + } + public static func parse_updateFavedStickers(_ reader: BufferReader) -> Update? { + return Api.Update.updateFavedStickers + } + public static func parse_updateFolderPeers(_ reader: BufferReader) -> Update? { + var _1: [Api.FolderPeer]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.FolderPeer.self) + } + 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.Update.updateFolderPeers(folderPeers: _1!, pts: _2!, ptsCount: _3!) + } + else { + return nil + } + } + public static func parse_updateGeoLiveViewed(_ reader: BufferReader) -> Update? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updateGeoLiveViewed(peer: _1!, msgId: _2!) + } + else { + return nil + } + } + public static func parse_updateGroupCall(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Api.GroupCall? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.GroupCall + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updateGroupCall(chatId: _1!, call: _2!) + } + else { + return nil + } + } + public static func parse_updateGroupCallConnection(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.DataJSON? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.DataJSON + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updateGroupCallConnection(flags: _1!, params: _2!) + } + else { + return nil + } + } + public static func parse_updateGroupCallParticipants(_ reader: BufferReader) -> Update? { + var _1: Api.InputGroupCall? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.InputGroupCall + } + var _2: [Api.GroupCallParticipant]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.GroupCallParticipant.self) + } + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updateGroupCallParticipants(call: _1!, participants: _2!, version: _3!) + } + else { + return nil + } + } + public static func parse_updateGroupInvitePrivacyForbidden(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + let _c1 = _1 != nil + if _c1 { + return Api.Update.updateGroupInvitePrivacyForbidden(userId: _1!) + } + else { + return nil + } + } + public static func parse_updateInlineBotCallbackQuery(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int64? + _3 = reader.readInt64() + var _4: Api.InputBotInlineMessageID? + if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.InputBotInlineMessageID + } + var _5: Int64? + _5 = reader.readInt64() + var _6: Buffer? + if Int(_1!) & Int(1 << 0) != 0 {_6 = parseBytes(reader) } + var _7: String? + if Int(_1!) & Int(1 << 1) != 0 {_7 = parseString(reader) } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.Update.updateInlineBotCallbackQuery(flags: _1!, queryId: _2!, userId: _3!, msgId: _4!, chatInstance: _5!, data: _6, gameShortName: _7) + } + else { + return nil + } + } + public static func parse_updateLangPack(_ reader: BufferReader) -> Update? { + var _1: Api.LangPackDifference? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.LangPackDifference + } + let _c1 = _1 != nil + if _c1 { + return Api.Update.updateLangPack(difference: _1!) + } + else { + return nil + } + } + public static func parse_updateLangPackTooLong(_ reader: BufferReader) -> Update? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.Update.updateLangPackTooLong(langCode: _1!) + } + else { + return nil + } + } + public static func parse_updateLoginToken(_ reader: BufferReader) -> Update? { + return Api.Update.updateLoginToken + } + public static func parse_updateMessageExtendedMedia(_ reader: BufferReader) -> Update? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _2: Int32? + _2 = reader.readInt32() + var _3: Api.MessageExtendedMedia? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.MessageExtendedMedia + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updateMessageExtendedMedia(peer: _1!, msgId: _2!, extendedMedia: _3!) + } + else { + return nil + } + } + public static func parse_updateMessageID(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updateMessageID(id: _1!, randomId: _2!) + } + else { + return nil + } + } + public static func parse_updateMessagePoll(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Api.Poll? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.Poll + } } + var _4: Api.PollResults? + if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.PollResults + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.Update.updateMessagePoll(flags: _1!, pollId: _2!, poll: _3, results: _4!) + } + else { + return nil + } + } + public static func parse_updateMessagePollVote(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Api.Peer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _3: [Buffer]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: Buffer.self) + } + 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.Update.updateMessagePollVote(pollId: _1!, peer: _2!, options: _3!, qts: _4!) + } + else { + return nil + } + } + public static func parse_updateMessageReactions(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Peer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt32() } + var _5: Api.MessageReactions? + if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.MessageReactions + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.Update.updateMessageReactions(flags: _1!, peer: _2!, msgId: _3!, topMsgId: _4, reactions: _5!) + } + else { + return nil + } + } + public static func parse_updateMoveStickerSetToTop(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updateMoveStickerSetToTop(flags: _1!, stickerset: _2!) + } + else { + return nil + } + } + public static func parse_updateNewAuthorization(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } + var _4: String? + if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) } + var _5: String? + if Int(_1!) & Int(1 << 0) != 0 {_5 = parseString(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 << 0) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.Update.updateNewAuthorization(flags: _1!, hash: _2!, date: _3, device: _4, location: _5) + } + else { + return nil + } + } + public static func parse_updateNewChannelMessage(_ reader: BufferReader) -> Update? { + var _1: Api.Message? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Message + } + 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.Update.updateNewChannelMessage(message: _1!, pts: _2!, ptsCount: _3!) + } + else { + return nil + } + } + public static func parse_updateNewEncryptedMessage(_ reader: BufferReader) -> Update? { + var _1: Api.EncryptedMessage? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.EncryptedMessage + } + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updateNewEncryptedMessage(message: _1!, qts: _2!) + } + else { + return nil + } + } + public static func parse_updateNewMessage(_ reader: BufferReader) -> Update? { + var _1: Api.Message? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Message + } + 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.Update.updateNewMessage(message: _1!, pts: _2!, ptsCount: _3!) + } + else { + return nil + } + } + public static func parse_updateNewScheduledMessage(_ reader: BufferReader) -> Update? { + var _1: Api.Message? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Message + } + let _c1 = _1 != nil + if _c1 { + return Api.Update.updateNewScheduledMessage(message: _1!) + } + else { + return nil + } + } + public static func parse_updateNewStickerSet(_ reader: BufferReader) -> Update? { + var _1: Api.messages.StickerSet? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.messages.StickerSet + } + let _c1 = _1 != nil + if _c1 { + return Api.Update.updateNewStickerSet(stickerset: _1!) + } + else { + return nil + } + } + public static func parse_updateNotifySettings(_ reader: BufferReader) -> Update? { + var _1: Api.NotifyPeer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.NotifyPeer + } + var _2: Api.PeerNotifySettings? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updateNotifySettings(peer: _1!, notifySettings: _2!) + } + else { + return nil + } + } + public static func parse_updatePeerBlocked(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Peer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Peer + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updatePeerBlocked(flags: _1!, peerId: _2!) + } + else { + return nil + } + } + public static func parse_updatePeerHistoryTTL(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Peer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _3: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updatePeerHistoryTTL(flags: _1!, peer: _2!, ttlPeriod: _3) + } + else { + return nil + } + } + public static func parse_updatePeerLocated(_ reader: BufferReader) -> Update? { + var _1: [Api.PeerLocated]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PeerLocated.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.Update.updatePeerLocated(peers: _1!) + } + else { + return nil + } + } + public static func parse_updatePeerSettings(_ reader: BufferReader) -> Update? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _2: Api.PeerSettings? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.PeerSettings + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updatePeerSettings(peer: _1!, settings: _2!) + } + else { + return nil + } + } + public static func parse_updatePeerWallpaper(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Peer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _3: Api.WallPaper? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.WallPaper + } } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updatePeerWallpaper(flags: _1!, peer: _2!, wallpaper: _3) + } + else { + return nil + } + } + public static func parse_updatePendingJoinRequests(_ reader: BufferReader) -> Update? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } var _2: Int32? _2 = reader.readInt32() - var _3: Int64? - _3 = reader.readInt64() - var _4: Int64? - if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt64() } - var _5: String? - if Int(_1!) & Int(1 << 1) != 0 {_5 = parseString(reader) } - var _6: String? - if Int(_1!) & Int(1 << 2) != 0 {_6 = parseString(reader) } - var _7: String? - if Int(_1!) & Int(1 << 3) != 0 {_7 = parseString(reader) } - var _8: String? - if Int(_1!) & Int(1 << 4) != 0 {_8 = parseString(reader) } - var _9: Api.UserProfilePhoto? - if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() { - _9 = Api.parse(reader, signature: signature) as? Api.UserProfilePhoto - } } - var _10: Api.UserStatus? - if Int(_1!) & Int(1 << 6) != 0 {if let signature = reader.readInt32() { - _10 = Api.parse(reader, signature: signature) as? Api.UserStatus - } } - var _11: Int32? - if Int(_1!) & Int(1 << 14) != 0 {_11 = reader.readInt32() } - var _12: [Api.RestrictionReason]? - if Int(_1!) & Int(1 << 18) != 0 {if let _ = reader.readInt32() { - _12 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self) - } } - var _13: String? - if Int(_1!) & Int(1 << 19) != 0 {_13 = parseString(reader) } - var _14: String? - if Int(_1!) & Int(1 << 22) != 0 {_14 = parseString(reader) } - var _15: Api.EmojiStatus? - if Int(_1!) & Int(1 << 30) != 0 {if let signature = reader.readInt32() { - _15 = Api.parse(reader, signature: signature) as? Api.EmojiStatus - } } - var _16: [Api.Username]? - if Int(_2!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { - _16 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Username.self) - } } - var _17: Int32? - if Int(_2!) & Int(1 << 5) != 0 {_17 = reader.readInt32() } - var _18: Int32? - if Int(_2!) & Int(1 << 7) != 0 {_18 = reader.readInt32() } - var _19: Int64? - if Int(_2!) & Int(1 << 6) != 0 {_19 = reader.readInt64() } + var _3: [Int64]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } 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 - 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 << 4) == 0) || _8 != nil - let _c9 = (Int(_1!) & Int(1 << 5) == 0) || _9 != nil - let _c10 = (Int(_1!) & Int(1 << 6) == 0) || _10 != nil - let _c11 = (Int(_1!) & Int(1 << 14) == 0) || _11 != nil - let _c12 = (Int(_1!) & Int(1 << 18) == 0) || _12 != nil - let _c13 = (Int(_1!) & Int(1 << 19) == 0) || _13 != nil - let _c14 = (Int(_1!) & Int(1 << 22) == 0) || _14 != nil - let _c15 = (Int(_1!) & Int(1 << 30) == 0) || _15 != nil - let _c16 = (Int(_2!) & Int(1 << 0) == 0) || _16 != nil - let _c17 = (Int(_2!) & Int(1 << 5) == 0) || _17 != nil - let _c18 = (Int(_2!) & Int(1 << 7) == 0) || _18 != nil - let _c19 = (Int(_2!) & Int(1 << 6) == 0) || _19 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 { - return Api.User.user(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, firstName: _5, lastName: _6, username: _7, phone: _8, photo: _9, status: _10, botInfoVersion: _11, restrictionReason: _12, botInlinePlaceholder: _13, langCode: _14, emojiStatus: _15, usernames: _16, storiesMaxId: _17, color: _18, backgroundEmojiId: _19) + if _c1 && _c2 && _c3 { + return Api.Update.updatePendingJoinRequests(peer: _1!, requestsPending: _2!, recentRequesters: _3!) + } + else { + return nil + } + } + public static func parse_updatePhoneCall(_ reader: BufferReader) -> Update? { + var _1: Api.PhoneCall? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.PhoneCall + } + let _c1 = _1 != nil + if _c1 { + return Api.Update.updatePhoneCall(phoneCall: _1!) } else { return nil } } - public static func parse_userEmpty(_ reader: BufferReader) -> User? { + public static func parse_updatePhoneCallSignalingData(_ reader: BufferReader) -> Update? { var _1: Int64? _1 = reader.readInt64() + var _2: Buffer? + _2 = parseBytes(reader) let _c1 = _1 != nil - if _c1 { - return Api.User.userEmpty(id: _1!) + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updatePhoneCallSignalingData(phoneCallId: _1!, data: _2!) } else { return nil } } - - } -} -public extension Api { - enum UserFull: TypeConstructorDescription { - case userFull(flags: 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?, premiumGifts: [Api.PremiumGiftOption]?, wallpaper: Api.WallPaper?, stories: Api.PeerStories?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .userFull(let flags, 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 premiumGifts, let wallpaper, let stories): - if boxed { - buffer.appendInt32(-1179571092) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeString(about!, buffer: buffer, boxed: false)} - settings.serialize(buffer, true) - if Int(flags) & Int(1 << 21) != 0 {personalPhoto!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {profilePhoto!.serialize(buffer, true)} - if Int(flags) & Int(1 << 22) != 0 {fallbackPhoto!.serialize(buffer, true)} - notifySettings.serialize(buffer, true) - if Int(flags) & Int(1 << 3) != 0 {botInfo!.serialize(buffer, true)} - if Int(flags) & Int(1 << 6) != 0 {serializeInt32(pinnedMsgId!, buffer: buffer, boxed: false)} - 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 << 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)} - if Int(flags) & Int(1 << 19) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(premiumGifts!.count)) - for item in premiumGifts! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 24) != 0 {wallpaper!.serialize(buffer, true)} - if Int(flags) & Int(1 << 25) != 0 {stories!.serialize(buffer, true)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .userFull(let flags, 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 premiumGifts, let wallpaper, let stories): - return ("userFull", [("flags", flags 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), ("premiumGifts", premiumGifts as Any), ("wallpaper", wallpaper as Any), ("stories", stories as Any)]) - } - } - - public static func parse_userFull(_ reader: BufferReader) -> UserFull? { + public static func parse_updatePinnedChannelMessages(_ reader: BufferReader) -> Update? { var _1: Int32? _1 = reader.readInt32() var _2: Int64? _2 = reader.readInt64() - var _3: String? - if Int(_1!) & Int(1 << 1) != 0 {_3 = parseString(reader) } - var _4: Api.PeerSettings? - if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.PeerSettings + var _3: [Int32]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) } - var _5: Api.Photo? - if Int(_1!) & Int(1 << 21) != 0 {if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.Photo - } } - var _6: Api.Photo? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.Photo - } } - var _7: Api.Photo? - if Int(_1!) & Int(1 << 22) != 0 {if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.Photo + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() + 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.Update.updatePinnedChannelMessages(flags: _1!, channelId: _2!, messages: _3!, pts: _4!, ptsCount: _5!) + } + else { + return nil + } + } + public static func parse_updatePinnedDialogs(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() } + var _3: [Api.DialogPeer]? + if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DialogPeer.self) } } - var _8: Api.PeerNotifySettings? + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updatePinnedDialogs(flags: _1!, folderId: _2, order: _3) + } + else { + return nil + } + } + public static func parse_updatePinnedMessages(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Peer? if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings + _2 = Api.parse(reader, signature: signature) as? Api.Peer } - var _9: Api.BotInfo? - if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { - _9 = Api.parse(reader, signature: signature) as? Api.BotInfo - } } - var _10: Int32? - if Int(_1!) & Int(1 << 6) != 0 {_10 = reader.readInt32() } - var _11: Int32? - _11 = reader.readInt32() - var _12: Int32? - if Int(_1!) & Int(1 << 11) != 0 {_12 = reader.readInt32() } - var _13: Int32? - if Int(_1!) & Int(1 << 14) != 0 {_13 = reader.readInt32() } - var _14: String? - if Int(_1!) & Int(1 << 15) != 0 {_14 = parseString(reader) } - var _15: String? - if Int(_1!) & Int(1 << 16) != 0 {_15 = parseString(reader) } - var _16: Api.ChatAdminRights? - if Int(_1!) & Int(1 << 17) != 0 {if let signature = reader.readInt32() { - _16 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights - } } - var _17: Api.ChatAdminRights? - if Int(_1!) & Int(1 << 18) != 0 {if let signature = reader.readInt32() { - _17 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights - } } - var _18: [Api.PremiumGiftOption]? - if Int(_1!) & Int(1 << 19) != 0 {if let _ = reader.readInt32() { - _18 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PremiumGiftOption.self) - } } - var _19: Api.WallPaper? - if Int(_1!) & Int(1 << 24) != 0 {if let signature = reader.readInt32() { - _19 = Api.parse(reader, signature: signature) as? Api.WallPaper - } } - var _20: Api.PeerStories? - if Int(_1!) & Int(1 << 25) != 0 {if let signature = reader.readInt32() { - _20 = Api.parse(reader, signature: signature) as? Api.PeerStories - } } + var _3: [Int32]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() + 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.Update.updatePinnedMessages(flags: _1!, peer: _2!, messages: _3!, pts: _4!, ptsCount: _5!) + } + else { + return nil + } + } + public static func parse_updatePrivacy(_ reader: BufferReader) -> Update? { + var _1: Api.PrivacyKey? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.PrivacyKey + } + var _2: [Api.PrivacyRule]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PrivacyRule.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updatePrivacy(key: _1!, rules: _2!) + } + else { + return nil + } + } + public static func parse_updatePtsChanged(_ reader: BufferReader) -> Update? { + return Api.Update.updatePtsChanged + } + public static func parse_updateReadChannelDiscussionInbox(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + var _5: Int64? + if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt64() } + 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 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.Update.updateReadChannelDiscussionInbox(flags: _1!, channelId: _2!, topMsgId: _3!, readMaxId: _4!, broadcastId: _5, broadcastPost: _6) + } + else { + return nil + } + } + public static func parse_updateReadChannelDiscussionOutbox(_ reader: BufferReader) -> Update? { + 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.Update.updateReadChannelDiscussionOutbox(channelId: _1!, topMsgId: _2!, readMaxId: _3!) + } + else { + return nil + } + } + public static func parse_updateReadChannelInbox(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } + var _3: Int64? + _3 = reader.readInt64() + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() + var _6: Int32? + _6 = reader.readInt32() + 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 + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.Update.updateReadChannelInbox(flags: _1!, folderId: _2, channelId: _3!, maxId: _4!, stillUnreadCount: _5!, pts: _6!) + } + else { + return nil + } + } + public static func parse_updateReadChannelOutbox(_ reader: BufferReader) -> Update? { + 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.Update.updateReadChannelOutbox(channelId: _1!, maxId: _2!) + } + else { + return nil + } + } + public static func parse_updateReadFeaturedEmojiStickers(_ reader: BufferReader) -> Update? { + return Api.Update.updateReadFeaturedEmojiStickers + } + public static func parse_updateReadFeaturedStickers(_ reader: BufferReader) -> Update? { + return Api.Update.updateReadFeaturedStickers + } + public static func parse_updateReadHistoryInbox(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } + 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: Int32? + _5 = reader.readInt32() + var _6: Int32? + _6 = reader.readInt32() + var _7: Int32? + _7 = reader.readInt32() + 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 + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.Update.updateReadHistoryInbox(flags: _1!, folderId: _2, peer: _3!, maxId: _4!, stillUnreadCount: _5!, pts: _6!, ptsCount: _7!) + } + else { + return nil + } + } + public static func parse_updateReadHistoryOutbox(_ reader: BufferReader) -> Update? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + 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 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c3 = _3 != nil let _c4 = _4 != nil - let _c5 = (Int(_1!) & Int(1 << 21) == 0) || _5 != nil - let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 22) == 0) || _7 != nil - let _c8 = _8 != nil - let _c9 = (Int(_1!) & Int(1 << 3) == 0) || _9 != nil - let _c10 = (Int(_1!) & Int(1 << 6) == 0) || _10 != nil - let _c11 = _11 != nil - let _c12 = (Int(_1!) & Int(1 << 11) == 0) || _12 != nil - let _c13 = (Int(_1!) & Int(1 << 14) == 0) || _13 != nil - let _c14 = (Int(_1!) & Int(1 << 15) == 0) || _14 != nil - let _c15 = (Int(_1!) & Int(1 << 16) == 0) || _15 != nil - let _c16 = (Int(_1!) & Int(1 << 17) == 0) || _16 != nil - let _c17 = (Int(_1!) & Int(1 << 18) == 0) || _17 != nil - let _c18 = (Int(_1!) & Int(1 << 19) == 0) || _18 != nil - let _c19 = (Int(_1!) & Int(1 << 24) == 0) || _19 != nil - let _c20 = (Int(_1!) & Int(1 << 25) == 0) || _20 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 { - return Api.UserFull.userFull(flags: _1!, id: _2!, about: _3, settings: _4!, personalPhoto: _5, profilePhoto: _6, fallbackPhoto: _7, notifySettings: _8!, botInfo: _9, pinnedMsgId: _10, commonChatsCount: _11!, folderId: _12, ttlPeriod: _13, themeEmoticon: _14, privateForwardName: _15, botGroupAdminRights: _16, botBroadcastAdminRights: _17, premiumGifts: _18, wallpaper: _19, stories: _20) + if _c1 && _c2 && _c3 && _c4 { + return Api.Update.updateReadHistoryOutbox(peer: _1!, maxId: _2!, pts: _3!, ptsCount: _4!) } else { return nil } } - - } -} -public extension Api { - enum UserProfilePhoto: TypeConstructorDescription { - case userProfilePhoto(flags: Int32, photoId: Int64, strippedThumb: Buffer?, dcId: Int32) - case userProfilePhotoEmpty - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .userProfilePhoto(let flags, let photoId, let strippedThumb, let dcId): - if boxed { - buffer.appendInt32(-2100168954) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(photoId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeBytes(strippedThumb!, buffer: buffer, boxed: false)} - serializeInt32(dcId, buffer: buffer, boxed: false) - break - case .userProfilePhotoEmpty: - if boxed { - buffer.appendInt32(1326562017) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .userProfilePhoto(let flags, let photoId, let strippedThumb, let dcId): - return ("userProfilePhoto", [("flags", flags as Any), ("photoId", photoId as Any), ("strippedThumb", strippedThumb as Any), ("dcId", dcId as Any)]) - case .userProfilePhotoEmpty: - return ("userProfilePhotoEmpty", []) - } - } - - public static func parse_userProfilePhoto(_ reader: BufferReader) -> UserProfilePhoto? { + public static func parse_updateReadMessagesContents(_ reader: BufferReader) -> Update? { var _1: Int32? _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Buffer? - if Int(_1!) & Int(1 << 1) != 0 {_3 = parseBytes(reader) } + var _2: [Int32]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + var _3: Int32? + _3 = reader.readInt32() var _4: Int32? _4 = reader.readInt32() + var _5: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt32() } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.UserProfilePhoto.userProfilePhoto(flags: _1!, photoId: _2!, strippedThumb: _3, dcId: _4!) + let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.Update.updateReadMessagesContents(flags: _1!, messages: _2!, pts: _3!, ptsCount: _4!, date: _5) } else { return nil } } - public static func parse_userProfilePhotoEmpty(_ reader: BufferReader) -> UserProfilePhoto? { - return Api.UserProfilePhoto.userProfilePhotoEmpty + public static func parse_updateReadStories(_ reader: BufferReader) -> Update? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updateReadStories(peer: _1!, maxId: _2!) + } + else { + return nil + } } - - } -} -public extension Api { - enum UserStatus: TypeConstructorDescription { - case userStatusEmpty - case userStatusLastMonth - case userStatusLastWeek - case userStatusOffline(wasOnline: Int32) - case userStatusOnline(expires: Int32) - case userStatusRecently - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .userStatusEmpty: - if boxed { - buffer.appendInt32(164646985) - } - - break - case .userStatusLastMonth: - if boxed { - buffer.appendInt32(2011940674) - } - - break - case .userStatusLastWeek: - if boxed { - buffer.appendInt32(129960444) - } - - break - case .userStatusOffline(let wasOnline): - if boxed { - buffer.appendInt32(9203775) - } - serializeInt32(wasOnline, buffer: buffer, boxed: false) - break - case .userStatusOnline(let expires): - if boxed { - buffer.appendInt32(-306628279) - } - serializeInt32(expires, buffer: buffer, boxed: false) - break - case .userStatusRecently: - if boxed { - buffer.appendInt32(-496024847) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .userStatusEmpty: - return ("userStatusEmpty", []) - case .userStatusLastMonth: - return ("userStatusLastMonth", []) - case .userStatusLastWeek: - return ("userStatusLastWeek", []) - case .userStatusOffline(let wasOnline): - return ("userStatusOffline", [("wasOnline", wasOnline as Any)]) - case .userStatusOnline(let expires): - return ("userStatusOnline", [("expires", expires as Any)]) - case .userStatusRecently: - return ("userStatusRecently", []) - } - } - - public static func parse_userStatusEmpty(_ reader: BufferReader) -> UserStatus? { - return Api.UserStatus.userStatusEmpty + public static func parse_updateRecentEmojiStatuses(_ reader: BufferReader) -> Update? { + return Api.Update.updateRecentEmojiStatuses } - public static func parse_userStatusLastMonth(_ reader: BufferReader) -> UserStatus? { - return Api.UserStatus.userStatusLastMonth + public static func parse_updateRecentReactions(_ reader: BufferReader) -> Update? { + return Api.Update.updateRecentReactions } - public static func parse_userStatusLastWeek(_ reader: BufferReader) -> UserStatus? { - return Api.UserStatus.userStatusLastWeek + public static func parse_updateRecentStickers(_ reader: BufferReader) -> Update? { + return Api.Update.updateRecentStickers } - public static func parse_userStatusOffline(_ reader: BufferReader) -> UserStatus? { - var _1: Int32? - _1 = reader.readInt32() + public static func parse_updateSavedGifs(_ reader: BufferReader) -> Update? { + return Api.Update.updateSavedGifs + } + public static func parse_updateSavedRingtones(_ reader: BufferReader) -> Update? { + return Api.Update.updateSavedRingtones + } + public static func parse_updateSentStoryReaction(_ reader: BufferReader) -> Update? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _2: Int32? + _2 = reader.readInt32() + var _3: Api.Reaction? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.Reaction + } let _c1 = _1 != nil - if _c1 { - return Api.UserStatus.userStatusOffline(wasOnline: _1!) + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updateSentStoryReaction(peer: _1!, storyId: _2!, reaction: _3!) } else { return nil } } - public static func parse_userStatusOnline(_ reader: BufferReader) -> UserStatus? { + public static func parse_updateServiceNotification(_ reader: BufferReader) -> Update? { var _1: Int32? _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() } + var _3: String? + _3 = parseString(reader) + var _4: String? + _4 = parseString(reader) + var _5: Api.MessageMedia? + if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.MessageMedia + } + var _6: [Api.MessageEntity]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) + } let _c1 = _1 != nil - if _c1 { - return Api.UserStatus.userStatusOnline(expires: _1!) + let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _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.Update.updateServiceNotification(flags: _1!, inboxDate: _2, type: _3!, message: _4!, media: _5!, entities: _6!) } else { return nil } } - public static func parse_userStatusRecently(_ reader: BufferReader) -> UserStatus? { - return Api.UserStatus.userStatusRecently - } - - } -} -public extension Api { - enum Username: TypeConstructorDescription { - case username(flags: Int32, username: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .username(let flags, let username): - if boxed { - buffer.appendInt32(-1274595769) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(username, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .username(let flags, let username): - return ("username", [("flags", flags as Any), ("username", username as Any)]) - } - } - - public static func parse_username(_ reader: BufferReader) -> Username? { + public static func parse_updateStickerSets(_ reader: BufferReader) -> Update? { var _1: Int32? _1 = reader.readInt32() - var _2: String? - _2 = parseString(reader) let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Username.username(flags: _1!, username: _2!) + if _c1 { + return Api.Update.updateStickerSets(flags: _1!) } else { return nil } } - - } -} -public extension Api { - enum VideoSize: TypeConstructorDescription { - case videoSize(flags: Int32, type: String, w: Int32, h: Int32, size: Int32, videoStartTs: Double?) - case videoSizeEmojiMarkup(emojiId: Int64, backgroundColors: [Int32]) - case videoSizeStickerMarkup(stickerset: Api.InputStickerSet, stickerId: Int64, backgroundColors: [Int32]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .videoSize(let flags, let type, let w, let h, let size, let videoStartTs): - if boxed { - buffer.appendInt32(-567037804) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(type, buffer: buffer, boxed: false) - serializeInt32(w, buffer: buffer, boxed: false) - serializeInt32(h, buffer: buffer, boxed: false) - serializeInt32(size, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeDouble(videoStartTs!, buffer: buffer, boxed: false)} - break - case .videoSizeEmojiMarkup(let emojiId, let backgroundColors): - if boxed { - buffer.appendInt32(-128171716) - } - serializeInt64(emojiId, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(backgroundColors.count)) - for item in backgroundColors { - serializeInt32(item, buffer: buffer, boxed: false) - } - break - case .videoSizeStickerMarkup(let stickerset, let stickerId, let backgroundColors): - if boxed { - buffer.appendInt32(228623102) - } - stickerset.serialize(buffer, true) - serializeInt64(stickerId, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(backgroundColors.count)) - for item in backgroundColors { - serializeInt32(item, buffer: buffer, boxed: false) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .videoSize(let flags, let type, let w, let h, let size, let videoStartTs): - return ("videoSize", [("flags", flags as Any), ("type", type as Any), ("w", w as Any), ("h", h as Any), ("size", size as Any), ("videoStartTs", videoStartTs as Any)]) - case .videoSizeEmojiMarkup(let emojiId, let backgroundColors): - return ("videoSizeEmojiMarkup", [("emojiId", emojiId as Any), ("backgroundColors", backgroundColors as Any)]) - case .videoSizeStickerMarkup(let stickerset, let stickerId, let backgroundColors): - return ("videoSizeStickerMarkup", [("stickerset", stickerset as Any), ("stickerId", stickerId as Any), ("backgroundColors", backgroundColors as Any)]) - } - } - - public static func parse_videoSize(_ reader: BufferReader) -> VideoSize? { + public static func parse_updateStickerSetsOrder(_ reader: BufferReader) -> Update? { var _1: Int32? _1 = reader.readInt32() - var _2: String? - _2 = parseString(reader) - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - _4 = reader.readInt32() - var _5: Int32? - _5 = reader.readInt32() - var _6: Double? - if Int(_1!) & Int(1 << 0) != 0 {_6 = reader.readDouble() } + var _2: [Int64]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.VideoSize.videoSize(flags: _1!, type: _2!, w: _3!, h: _4!, size: _5!, videoStartTs: _6) + if _c1 && _c2 { + return Api.Update.updateStickerSetsOrder(flags: _1!, order: _2!) } else { return nil } } - public static func parse_videoSizeEmojiMarkup(_ reader: BufferReader) -> VideoSize? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Int32]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + public static func parse_updateStoriesStealthMode(_ reader: BufferReader) -> Update? { + var _1: Api.StoriesStealthMode? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.StoriesStealthMode } let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.VideoSize.videoSizeEmojiMarkup(emojiId: _1!, backgroundColors: _2!) + if _c1 { + return Api.Update.updateStoriesStealthMode(stealthMode: _1!) } else { return nil } } - public static func parse_videoSizeStickerMarkup(_ reader: BufferReader) -> VideoSize? { - var _1: Api.InputStickerSet? + public static func parse_updateStory(_ reader: BufferReader) -> Update? { + var _1: Api.Peer? if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.InputStickerSet + _1 = Api.parse(reader, signature: signature) as? Api.Peer } - var _2: Int64? - _2 = reader.readInt64() - var _3: [Int32]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + var _2: Api.StoryItem? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.StoryItem } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.VideoSize.videoSizeStickerMarkup(stickerset: _1!, stickerId: _2!, backgroundColors: _3!) + if _c1 && _c2 { + return Api.Update.updateStory(peer: _1!, story: _2!) } else { return nil } } - - } -} -public extension Api { - enum WallPaper: TypeConstructorDescription { - case wallPaper(id: Int64, flags: Int32, accessHash: Int64, slug: String, document: Api.Document, settings: Api.WallPaperSettings?) - case wallPaperNoFile(id: Int64, flags: Int32, settings: Api.WallPaperSettings?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .wallPaper(let id, let flags, let accessHash, let slug, let document, let settings): - if boxed { - buffer.appendInt32(-1539849235) - } - serializeInt64(id, buffer: buffer, boxed: false) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(accessHash, buffer: buffer, boxed: false) - serializeString(slug, buffer: buffer, boxed: false) - document.serialize(buffer, true) - if Int(flags) & Int(1 << 2) != 0 {settings!.serialize(buffer, true)} - break - case .wallPaperNoFile(let id, let flags, let settings): - if boxed { - buffer.appendInt32(-528465642) - } - serializeInt64(id, buffer: buffer, boxed: false) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {settings!.serialize(buffer, true)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .wallPaper(let id, let flags, let accessHash, let slug, let document, let settings): - return ("wallPaper", [("id", id as Any), ("flags", flags as Any), ("accessHash", accessHash as Any), ("slug", slug as Any), ("document", document as Any), ("settings", settings as Any)]) - case .wallPaperNoFile(let id, let flags, let settings): - return ("wallPaperNoFile", [("id", id as Any), ("flags", flags as Any), ("settings", settings as Any)]) - } - } - - public static func parse_wallPaper(_ reader: BufferReader) -> WallPaper? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int64? - _3 = reader.readInt64() - var _4: String? - _4 = parseString(reader) - var _5: Api.Document? - if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.Document - } - var _6: Api.WallPaperSettings? - if Int(_2!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.WallPaperSettings - } } + public static func parse_updateStoryID(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = (Int(_2!) & Int(1 << 2) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.WallPaper.wallPaper(id: _1!, flags: _2!, accessHash: _3!, slug: _4!, document: _5!, settings: _6) + if _c1 && _c2 { + return Api.Update.updateStoryID(id: _1!, randomId: _2!) } else { return nil } } - public static func parse_wallPaperNoFile(_ reader: BufferReader) -> WallPaper? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int32? - _2 = reader.readInt32() - var _3: Api.WallPaperSettings? - if Int(_2!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.WallPaperSettings - } } + public static func parse_updateTheme(_ reader: BufferReader) -> Update? { + var _1: Api.Theme? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Theme + } let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_2!) & Int(1 << 2) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.WallPaper.wallPaperNoFile(id: _1!, flags: _2!, settings: _3) + if _c1 { + return Api.Update.updateTheme(theme: _1!) } else { return nil } } - - } -} -public extension Api { - enum WallPaperSettings: TypeConstructorDescription { - case wallPaperSettings(flags: Int32, backgroundColor: Int32?, secondBackgroundColor: Int32?, thirdBackgroundColor: Int32?, fourthBackgroundColor: Int32?, intensity: Int32?, rotation: Int32?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .wallPaperSettings(let flags, let backgroundColor, let secondBackgroundColor, let thirdBackgroundColor, let fourthBackgroundColor, let intensity, let rotation): - if boxed { - buffer.appendInt32(499236004) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(backgroundColor!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {serializeInt32(secondBackgroundColor!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 5) != 0 {serializeInt32(thirdBackgroundColor!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 6) != 0 {serializeInt32(fourthBackgroundColor!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {serializeInt32(intensity!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {serializeInt32(rotation!, buffer: buffer, boxed: false)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .wallPaperSettings(let flags, let backgroundColor, let secondBackgroundColor, let thirdBackgroundColor, let fourthBackgroundColor, let intensity, let rotation): - return ("wallPaperSettings", [("flags", flags as Any), ("backgroundColor", backgroundColor as Any), ("secondBackgroundColor", secondBackgroundColor as Any), ("thirdBackgroundColor", thirdBackgroundColor as Any), ("fourthBackgroundColor", fourthBackgroundColor as Any), ("intensity", intensity as Any), ("rotation", rotation as Any)]) - } - } - - public static func parse_wallPaperSettings(_ reader: BufferReader) -> WallPaperSettings? { + public static func parse_updateTranscribedAudio(_ reader: BufferReader) -> Update? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } + var _2: Api.Peer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Peer + } var _3: Int32? - if Int(_1!) & Int(1 << 4) != 0 {_3 = reader.readInt32() } - var _4: Int32? - if Int(_1!) & Int(1 << 5) != 0 {_4 = reader.readInt32() } - var _5: Int32? - if Int(_1!) & Int(1 << 6) != 0 {_5 = reader.readInt32() } - var _6: Int32? - if Int(_1!) & Int(1 << 3) != 0 {_6 = reader.readInt32() } - var _7: Int32? - if Int(_1!) & Int(1 << 4) != 0 {_7 = reader.readInt32() } + _3 = reader.readInt32() + var _4: Int64? + _4 = reader.readInt64() + var _5: String? + _5 = parseString(reader) let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - let _c3 = (Int(_1!) & Int(1 << 4) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 5) == 0) || _4 != nil - let _c5 = (Int(_1!) & Int(1 << 6) == 0) || _5 != nil - let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.WallPaperSettings.wallPaperSettings(flags: _1!, backgroundColor: _2, secondBackgroundColor: _3, thirdBackgroundColor: _4, fourthBackgroundColor: _5, intensity: _6, rotation: _7) + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.Update.updateTranscribedAudio(flags: _1!, peer: _2!, msgId: _3!, transcriptionId: _4!, text: _5!) } else { return nil } } - - } -} -public extension Api { - enum WebAuthorization: TypeConstructorDescription { - case webAuthorization(hash: Int64, botId: Int64, domain: String, browser: String, platform: String, dateCreated: Int32, dateActive: Int32, ip: String, region: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .webAuthorization(let hash, let botId, let domain, let browser, let platform, let dateCreated, let dateActive, let ip, let region): - if boxed { - buffer.appendInt32(-1493633966) - } - serializeInt64(hash, buffer: buffer, boxed: false) - serializeInt64(botId, buffer: buffer, boxed: false) - serializeString(domain, buffer: buffer, boxed: false) - serializeString(browser, buffer: buffer, boxed: false) - serializeString(platform, buffer: buffer, boxed: false) - serializeInt32(dateCreated, buffer: buffer, boxed: false) - serializeInt32(dateActive, buffer: buffer, boxed: false) - serializeString(ip, buffer: buffer, boxed: false) - serializeString(region, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .webAuthorization(let hash, let botId, let domain, let browser, let platform, let dateCreated, let dateActive, let ip, let region): - return ("webAuthorization", [("hash", hash as Any), ("botId", botId as Any), ("domain", domain as Any), ("browser", browser as Any), ("platform", platform as Any), ("dateCreated", dateCreated as Any), ("dateActive", dateActive as Any), ("ip", ip as Any), ("region", region as Any)]) - } - } - - public static func parse_webAuthorization(_ reader: BufferReader) -> WebAuthorization? { + public static func parse_updateUser(_ reader: BufferReader) -> Update? { var _1: Int64? _1 = reader.readInt64() - var _2: Int64? - _2 = reader.readInt64() - var _3: String? - _3 = parseString(reader) - var _4: String? - _4 = parseString(reader) - var _5: String? - _5 = parseString(reader) - var _6: Int32? - _6 = reader.readInt32() - var _7: Int32? - _7 = reader.readInt32() - var _8: String? - _8 = parseString(reader) - var _9: String? - _9 = 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 = _7 != nil - let _c8 = _8 != nil - let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.WebAuthorization.webAuthorization(hash: _1!, botId: _2!, domain: _3!, browser: _4!, platform: _5!, dateCreated: _6!, dateActive: _7!, ip: _8!, region: _9!) + if _c1 { + return Api.Update.updateUser(userId: _1!) } else { return nil } } - - } -} -public extension Api { - enum WebDocument: TypeConstructorDescription { - case webDocument(url: String, accessHash: Int64, size: Int32, mimeType: String, attributes: [Api.DocumentAttribute]) - case webDocumentNoProxy(url: String, size: Int32, mimeType: String, attributes: [Api.DocumentAttribute]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .webDocument(let url, let accessHash, let size, let mimeType, let attributes): - if boxed { - buffer.appendInt32(475467473) - } - serializeString(url, buffer: buffer, boxed: false) - serializeInt64(accessHash, buffer: buffer, boxed: false) - serializeInt32(size, buffer: buffer, boxed: false) - serializeString(mimeType, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(attributes.count)) - for item in attributes { - item.serialize(buffer, true) - } - break - case .webDocumentNoProxy(let url, let size, let mimeType, let attributes): - if boxed { - buffer.appendInt32(-104284986) - } - serializeString(url, buffer: buffer, boxed: false) - serializeInt32(size, buffer: buffer, boxed: false) - serializeString(mimeType, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(attributes.count)) - for item in attributes { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .webDocument(let url, let accessHash, let size, let mimeType, let attributes): - return ("webDocument", [("url", url as Any), ("accessHash", accessHash as Any), ("size", size as Any), ("mimeType", mimeType as Any), ("attributes", attributes as Any)]) - case .webDocumentNoProxy(let url, let size, let mimeType, let attributes): - return ("webDocumentNoProxy", [("url", url as Any), ("size", size as Any), ("mimeType", mimeType as Any), ("attributes", attributes as Any)]) - } - } - - public static func parse_webDocument(_ reader: BufferReader) -> WebDocument? { - var _1: String? - _1 = parseString(reader) - var _2: Int64? - _2 = reader.readInt64() - var _3: Int32? - _3 = reader.readInt32() - var _4: String? - _4 = parseString(reader) - var _5: [Api.DocumentAttribute]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DocumentAttribute.self) + public static func parse_updateUserEmojiStatus(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Api.EmojiStatus? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.EmojiStatus } 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.WebDocument.webDocument(url: _1!, accessHash: _2!, size: _3!, mimeType: _4!, attributes: _5!) + if _c1 && _c2 { + return Api.Update.updateUserEmojiStatus(userId: _1!, emojiStatus: _2!) } else { return nil } } - public static func parse_webDocumentNoProxy(_ reader: BufferReader) -> WebDocument? { - var _1: String? - _1 = parseString(reader) - var _2: Int32? - _2 = reader.readInt32() + public static func parse_updateUserName(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: String? + _2 = parseString(reader) var _3: String? _3 = parseString(reader) - var _4: [Api.DocumentAttribute]? + var _4: [Api.Username]? if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DocumentAttribute.self) + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Username.self) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil if _c1 && _c2 && _c3 && _c4 { - return Api.WebDocument.webDocumentNoProxy(url: _1!, size: _2!, mimeType: _3!, attributes: _4!) + return Api.Update.updateUserName(userId: _1!, firstName: _2!, lastName: _3!, usernames: _4!) } else { return nil } } - - } -} -public extension Api { - enum WebPage: TypeConstructorDescription { - case webPage(flags: Int32, id: Int64, url: String, displayUrl: String, hash: Int32, type: String?, siteName: String?, title: String?, description: String?, photo: Api.Photo?, embedUrl: String?, embedType: String?, embedWidth: Int32?, embedHeight: Int32?, duration: Int32?, author: String?, document: Api.Document?, cachedPage: Api.Page?, attributes: [Api.WebPageAttribute]?) - case webPageEmpty(flags: Int32, id: Int64, url: String?) - case webPageNotModified(flags: Int32, cachedPageViews: Int32?) - case webPagePending(flags: Int32, id: Int64, url: String?, date: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .webPage(let flags, let id, let url, let displayUrl, let hash, let type, let siteName, let title, let description, let photo, let embedUrl, let embedType, let embedWidth, let embedHeight, let duration, let author, let document, let cachedPage, let attributes): - if boxed { - buffer.appendInt32(-392411726) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(id, buffer: buffer, boxed: false) - serializeString(url, buffer: buffer, boxed: false) - serializeString(displayUrl, buffer: buffer, boxed: false) - serializeInt32(hash, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(type!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeString(siteName!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(title!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {serializeString(description!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {photo!.serialize(buffer, true)} - if Int(flags) & Int(1 << 5) != 0 {serializeString(embedUrl!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 5) != 0 {serializeString(embedType!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 6) != 0 {serializeInt32(embedWidth!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 6) != 0 {serializeInt32(embedHeight!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 7) != 0 {serializeInt32(duration!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 8) != 0 {serializeString(author!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 9) != 0 {document!.serialize(buffer, true)} - if Int(flags) & Int(1 << 10) != 0 {cachedPage!.serialize(buffer, true)} - if Int(flags) & Int(1 << 12) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(attributes!.count)) - for item in attributes! { - item.serialize(buffer, true) - }} - break - case .webPageEmpty(let flags, let id, let url): - if boxed { - buffer.appendInt32(555358088) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(url!, buffer: buffer, boxed: false)} - break - case .webPageNotModified(let flags, let cachedPageViews): - if boxed { - buffer.appendInt32(1930545681) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(cachedPageViews!, buffer: buffer, boxed: false)} - break - case .webPagePending(let flags, let id, let url, let date): - if boxed { - buffer.appendInt32(-1328464313) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(url!, buffer: buffer, boxed: false)} - serializeInt32(date, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .webPage(let flags, let id, let url, let displayUrl, let hash, let type, let siteName, let title, let description, let photo, let embedUrl, let embedType, let embedWidth, let embedHeight, let duration, let author, let document, let cachedPage, let attributes): - return ("webPage", [("flags", flags as Any), ("id", id as Any), ("url", url as Any), ("displayUrl", displayUrl as Any), ("hash", hash as Any), ("type", type as Any), ("siteName", siteName as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("embedUrl", embedUrl as Any), ("embedType", embedType as Any), ("embedWidth", embedWidth as Any), ("embedHeight", embedHeight as Any), ("duration", duration as Any), ("author", author as Any), ("document", document as Any), ("cachedPage", cachedPage as Any), ("attributes", attributes as Any)]) - case .webPageEmpty(let flags, let id, let url): - return ("webPageEmpty", [("flags", flags as Any), ("id", id as Any), ("url", url as Any)]) - case .webPageNotModified(let flags, let cachedPageViews): - return ("webPageNotModified", [("flags", flags as Any), ("cachedPageViews", cachedPageViews as Any)]) - case .webPagePending(let flags, let id, let url, let date): - return ("webPagePending", [("flags", flags as Any), ("id", id as Any), ("url", url as Any), ("date", date as Any)]) - } - } - - public static func parse_webPage(_ reader: BufferReader) -> WebPage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: String? - _3 = parseString(reader) - var _4: String? - _4 = parseString(reader) - var _5: Int32? - _5 = reader.readInt32() - var _6: String? - if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) } - var _7: String? - if Int(_1!) & Int(1 << 1) != 0 {_7 = parseString(reader) } - var _8: String? - if Int(_1!) & Int(1 << 2) != 0 {_8 = parseString(reader) } - var _9: String? - if Int(_1!) & Int(1 << 3) != 0 {_9 = parseString(reader) } - var _10: Api.Photo? - if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() { - _10 = Api.parse(reader, signature: signature) as? Api.Photo - } } - var _11: String? - if Int(_1!) & Int(1 << 5) != 0 {_11 = parseString(reader) } - var _12: String? - if Int(_1!) & Int(1 << 5) != 0 {_12 = parseString(reader) } - var _13: Int32? - if Int(_1!) & Int(1 << 6) != 0 {_13 = reader.readInt32() } - var _14: Int32? - if Int(_1!) & Int(1 << 6) != 0 {_14 = reader.readInt32() } - var _15: Int32? - if Int(_1!) & Int(1 << 7) != 0 {_15 = reader.readInt32() } - var _16: String? - if Int(_1!) & Int(1 << 8) != 0 {_16 = parseString(reader) } - var _17: Api.Document? - if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() { - _17 = Api.parse(reader, signature: signature) as? Api.Document - } } - var _18: Api.Page? - if Int(_1!) & Int(1 << 10) != 0 {if let signature = reader.readInt32() { - _18 = Api.parse(reader, signature: signature) as? Api.Page - } } - var _19: [Api.WebPageAttribute]? - if Int(_1!) & Int(1 << 12) != 0 {if let _ = reader.readInt32() { - _19 = Api.parseVector(reader, elementSignature: 0, elementType: Api.WebPageAttribute.self) - } } + public static func parse_updateUserPhone(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: String? + _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil - let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil - let _c9 = (Int(_1!) & Int(1 << 3) == 0) || _9 != nil - let _c10 = (Int(_1!) & Int(1 << 4) == 0) || _10 != nil - let _c11 = (Int(_1!) & Int(1 << 5) == 0) || _11 != nil - let _c12 = (Int(_1!) & Int(1 << 5) == 0) || _12 != nil - let _c13 = (Int(_1!) & Int(1 << 6) == 0) || _13 != nil - let _c14 = (Int(_1!) & Int(1 << 6) == 0) || _14 != nil - let _c15 = (Int(_1!) & Int(1 << 7) == 0) || _15 != nil - let _c16 = (Int(_1!) & Int(1 << 8) == 0) || _16 != nil - let _c17 = (Int(_1!) & Int(1 << 9) == 0) || _17 != nil - let _c18 = (Int(_1!) & Int(1 << 10) == 0) || _18 != nil - let _c19 = (Int(_1!) & Int(1 << 12) == 0) || _19 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 { - return Api.WebPage.webPage(flags: _1!, id: _2!, url: _3!, displayUrl: _4!, hash: _5!, type: _6, siteName: _7, title: _8, description: _9, photo: _10, embedUrl: _11, embedType: _12, embedWidth: _13, embedHeight: _14, duration: _15, author: _16, document: _17, cachedPage: _18, attributes: _19) + if _c1 && _c2 { + return Api.Update.updateUserPhone(userId: _1!, phone: _2!) } else { return nil } } - public static func parse_webPageEmpty(_ reader: BufferReader) -> WebPage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: String? - if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } + public static func parse_updateUserStatus(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Api.UserStatus? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.UserStatus + } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.WebPage.webPageEmpty(flags: _1!, id: _2!, url: _3) + if _c1 && _c2 { + return Api.Update.updateUserStatus(userId: _1!, status: _2!) } else { return nil } } - public static func parse_webPageNotModified(_ reader: BufferReader) -> WebPage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } + public static func parse_updateUserTyping(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Api.SendMessageAction? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.SendMessageAction + } let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c2 = _2 != nil if _c1 && _c2 { - return Api.WebPage.webPageNotModified(flags: _1!, cachedPageViews: _2) + return Api.Update.updateUserTyping(userId: _1!, action: _2!) } else { return nil } } - public static func parse_webPagePending(_ reader: BufferReader) -> WebPage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: String? - if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } - var _4: Int32? - _4 = reader.readInt32() + public static func parse_updateWebPage(_ reader: BufferReader) -> Update? { + var _1: Api.WebPage? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.WebPage + } + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.WebPage.webPagePending(flags: _1!, id: _2!, url: _3, date: _4!) + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updateWebPage(webpage: _1!, pts: _2!, ptsCount: _3!) + } + else { + return nil + } + } + public static func parse_updateWebViewResultSent(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + let _c1 = _1 != nil + if _c1 { + return Api.Update.updateWebViewResultSent(queryId: _1!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api23.swift b/submodules/TelegramApi/Sources/Api23.swift index 55a953bc119..a098cfed273 100644 --- a/submodules/TelegramApi/Sources/Api23.swift +++ b/submodules/TelegramApi/Sources/Api23.swift @@ -1,197 +1,111 @@ public extension Api { - indirect enum WebPageAttribute: TypeConstructorDescription { - case webPageAttributeStory(flags: Int32, peer: Api.Peer, id: Int32, story: Api.StoryItem?) - case webPageAttributeTheme(flags: Int32, documents: [Api.Document]?, settings: Api.ThemeSettings?) + indirect enum Updates: TypeConstructorDescription { + case updateShort(update: Api.Update, date: Int32) + case updateShortChatMessage(flags: Int32, id: Int32, fromId: Int64, chatId: Int64, message: String, pts: Int32, ptsCount: Int32, date: Int32, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, replyTo: Api.MessageReplyHeader?, entities: [Api.MessageEntity]?, ttlPeriod: Int32?) + case updateShortMessage(flags: Int32, id: Int32, userId: Int64, message: String, pts: Int32, ptsCount: Int32, date: Int32, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, replyTo: Api.MessageReplyHeader?, entities: [Api.MessageEntity]?, ttlPeriod: Int32?) + case updateShortSentMessage(flags: Int32, id: Int32, pts: Int32, ptsCount: Int32, date: Int32, media: Api.MessageMedia?, entities: [Api.MessageEntity]?, ttlPeriod: Int32?) + case updates(updates: [Api.Update], users: [Api.User], chats: [Api.Chat], date: Int32, seq: Int32) + case updatesCombined(updates: [Api.Update], users: [Api.User], chats: [Api.Chat], date: Int32, seqStart: Int32, seq: Int32) + case updatesTooLong public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .webPageAttributeStory(let flags, let peer, let id, let story): + case .updateShort(let update, let date): if boxed { - buffer.appendInt32(781501415) + buffer.appendInt32(2027216577) } - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {story!.serialize(buffer, true)} + update.serialize(buffer, true) + serializeInt32(date, buffer: buffer, boxed: false) break - case .webPageAttributeTheme(let flags, let documents, let settings): + case .updateShortChatMessage(let flags, let id, let fromId, let chatId, let message, let pts, let ptsCount, let date, let fwdFrom, let viaBotId, let replyTo, let entities, let ttlPeriod): if boxed { - buffer.appendInt32(1421174295) + buffer.appendInt32(1299050149) } serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(documents!.count)) - for item in documents! { + serializeInt32(id, buffer: buffer, boxed: false) + serializeInt64(fromId, buffer: buffer, boxed: false) + serializeInt64(chatId, buffer: buffer, boxed: false) + serializeString(message, buffer: buffer, boxed: false) + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {fwdFrom!.serialize(buffer, true)} + if Int(flags) & Int(1 << 11) != 0 {serializeInt64(viaBotId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {replyTo!.serialize(buffer, true)} + if Int(flags) & Int(1 << 7) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities!.count)) + for item in entities! { item.serialize(buffer, true) }} - if Int(flags) & Int(1 << 1) != 0 {settings!.serialize(buffer, true)} + if Int(flags) & Int(1 << 25) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)} break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .webPageAttributeStory(let flags, let peer, let id, let story): - return ("webPageAttributeStory", [("flags", flags as Any), ("peer", peer as Any), ("id", id as Any), ("story", story as Any)]) - case .webPageAttributeTheme(let flags, let documents, let settings): - return ("webPageAttributeTheme", [("flags", flags as Any), ("documents", documents as Any), ("settings", settings as Any)]) - } - } - - public static func parse_webPageAttributeStory(_ reader: BufferReader) -> WebPageAttribute? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.Peer? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _3: Int32? - _3 = reader.readInt32() - var _4: Api.StoryItem? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.StoryItem - } } - 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.WebPageAttribute.webPageAttributeStory(flags: _1!, peer: _2!, id: _3!, story: _4) - } - else { - return nil - } - } - public static func parse_webPageAttributeTheme(_ reader: BufferReader) -> WebPageAttribute? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.Document]? - if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) - } } - var _3: Api.ThemeSettings? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.ThemeSettings - } } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.WebPageAttribute.webPageAttributeTheme(flags: _1!, documents: _2, settings: _3) - } - else { - return nil - } - } - - } -} -public extension Api { - enum WebViewMessageSent: TypeConstructorDescription { - case webViewMessageSent(flags: Int32, msgId: Api.InputBotInlineMessageID?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .webViewMessageSent(let flags, let msgId): + case .updateShortMessage(let flags, let id, let userId, let message, let pts, let ptsCount, let date, let fwdFrom, let viaBotId, let replyTo, let entities, let ttlPeriod): if boxed { - buffer.appendInt32(211046684) + buffer.appendInt32(826001400) } serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {msgId!.serialize(buffer, true)} + serializeInt32(id, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + serializeString(message, buffer: buffer, boxed: false) + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {fwdFrom!.serialize(buffer, true)} + if Int(flags) & Int(1 << 11) != 0 {serializeInt64(viaBotId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {replyTo!.serialize(buffer, true)} + if Int(flags) & Int(1 << 7) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities!.count)) + for item in entities! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 25) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)} break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .webViewMessageSent(let flags, let msgId): - return ("webViewMessageSent", [("flags", flags as Any), ("msgId", msgId as Any)]) - } - } - - public static func parse_webViewMessageSent(_ reader: BufferReader) -> WebViewMessageSent? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.InputBotInlineMessageID? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.InputBotInlineMessageID - } } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.WebViewMessageSent.webViewMessageSent(flags: _1!, msgId: _2) - } - else { - return nil - } - } - - } -} -public extension Api { - enum WebViewResult: TypeConstructorDescription { - case webViewResultUrl(queryId: Int64, url: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .webViewResultUrl(let queryId, let url): + case .updateShortSentMessage(let flags, let id, let pts, let ptsCount, let date, let media, let entities, let ttlPeriod): if boxed { - buffer.appendInt32(202659196) + buffer.appendInt32(-1877614335) } - serializeInt64(queryId, buffer: buffer, boxed: false) - serializeString(url, buffer: buffer, boxed: false) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(id, buffer: buffer, boxed: false) + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 9) != 0 {media!.serialize(buffer, true)} + if Int(flags) & Int(1 << 7) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities!.count)) + for item in entities! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 25) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)} break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .webViewResultUrl(let queryId, let url): - return ("webViewResultUrl", [("queryId", queryId as Any), ("url", url as Any)]) - } - } - - public static func parse_webViewResultUrl(_ reader: BufferReader) -> WebViewResult? { - var _1: Int64? - _1 = reader.readInt64() - var _2: String? - _2 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.WebViewResult.webViewResultUrl(queryId: _1!, url: _2!) - } - else { - return nil - } - } - - } -} -public extension Api.account { - enum AuthorizationForm: TypeConstructorDescription { - case authorizationForm(flags: Int32, requiredTypes: [Api.SecureRequiredType], values: [Api.SecureValue], errors: [Api.SecureValueError], users: [Api.User], privacyPolicyUrl: String?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .authorizationForm(let flags, let requiredTypes, let values, let errors, let users, let privacyPolicyUrl): + case .updates(let updates, let users, let chats, let date, let seq): if boxed { - buffer.appendInt32(-1389486888) + buffer.appendInt32(1957577280) } - serializeInt32(flags, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(requiredTypes.count)) - for item in requiredTypes { + buffer.appendInt32(Int32(updates.count)) + for item in updates { item.serialize(buffer, true) } buffer.appendInt32(481674261) - buffer.appendInt32(Int32(values.count)) - for item in values { + buffer.appendInt32(Int32(users.count)) + for item in users { item.serialize(buffer, true) } buffer.appendInt32(481674261) - buffer.appendInt32(Int32(errors.count)) - for item in errors { + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt32(seq, buffer: buffer, boxed: false) + break + case .updatesCombined(let updates, let users, let chats, let date, let seqStart, let seq): + if boxed { + buffer.appendInt32(1918567619) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(updates.count)) + for item in updates { item.serialize(buffer, true) } buffer.appendInt32(481674261) @@ -199,215 +113,246 @@ public extension Api.account { for item in users { item.serialize(buffer, true) } - if Int(flags) & Int(1 << 0) != 0 {serializeString(privacyPolicyUrl!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt32(seqStart, buffer: buffer, boxed: false) + serializeInt32(seq, buffer: buffer, boxed: false) + break + case .updatesTooLong: + if boxed { + buffer.appendInt32(-484987010) + } + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .authorizationForm(let flags, let requiredTypes, let values, let errors, let users, let privacyPolicyUrl): - return ("authorizationForm", [("flags", flags as Any), ("requiredTypes", requiredTypes as Any), ("values", values as Any), ("errors", errors as Any), ("users", users as Any), ("privacyPolicyUrl", privacyPolicyUrl as Any)]) - } - } - - public static func parse_authorizationForm(_ reader: BufferReader) -> AuthorizationForm? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.SecureRequiredType]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureRequiredType.self) - } - var _3: [Api.SecureValue]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureValue.self) + case .updateShort(let update, let date): + return ("updateShort", [("update", update as Any), ("date", date as Any)]) + case .updateShortChatMessage(let flags, let id, let fromId, let chatId, let message, let pts, let ptsCount, let date, let fwdFrom, let viaBotId, let replyTo, let entities, let ttlPeriod): + return ("updateShortChatMessage", [("flags", flags as Any), ("id", id as Any), ("fromId", fromId as Any), ("chatId", chatId as Any), ("message", message as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any), ("date", date as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("replyTo", replyTo as Any), ("entities", entities as Any), ("ttlPeriod", ttlPeriod as Any)]) + case .updateShortMessage(let flags, let id, let userId, let message, let pts, let ptsCount, let date, let fwdFrom, let viaBotId, let replyTo, let entities, let ttlPeriod): + return ("updateShortMessage", [("flags", flags as Any), ("id", id as Any), ("userId", userId as Any), ("message", message as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any), ("date", date as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("replyTo", replyTo as Any), ("entities", entities as Any), ("ttlPeriod", ttlPeriod as Any)]) + case .updateShortSentMessage(let flags, let id, let pts, let ptsCount, let date, let media, let entities, let ttlPeriod): + return ("updateShortSentMessage", [("flags", flags as Any), ("id", id as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any), ("date", date as Any), ("media", media as Any), ("entities", entities as Any), ("ttlPeriod", ttlPeriod as Any)]) + case .updates(let updates, let users, let chats, let date, let seq): + return ("updates", [("updates", updates as Any), ("users", users as Any), ("chats", chats as Any), ("date", date as Any), ("seq", seq as Any)]) + case .updatesCombined(let updates, let users, let chats, let date, let seqStart, let seq): + return ("updatesCombined", [("updates", updates as Any), ("users", users as Any), ("chats", chats as Any), ("date", date as Any), ("seqStart", seqStart as Any), ("seq", seq as Any)]) + case .updatesTooLong: + return ("updatesTooLong", []) + } + } + + public static func parse_updateShort(_ reader: BufferReader) -> Updates? { + var _1: Api.Update? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Update } - var _4: [Api.SecureValueError]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureValueError.self) + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Updates.updateShort(update: _1!, date: _2!) } - var _5: [Api.User]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + else { + return nil } - var _6: String? - if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) } + } + public static func parse_updateShortChatMessage(_ reader: BufferReader) -> Updates? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int64? + _3 = reader.readInt64() + var _4: Int64? + _4 = reader.readInt64() + var _5: String? + _5 = parseString(reader) + var _6: Int32? + _6 = reader.readInt32() + var _7: Int32? + _7 = reader.readInt32() + var _8: Int32? + _8 = reader.readInt32() + var _9: Api.MessageFwdHeader? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.MessageFwdHeader + } } + var _10: Int64? + if Int(_1!) & Int(1 << 11) != 0 {_10 = reader.readInt64() } + var _11: Api.MessageReplyHeader? + if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { + _11 = Api.parse(reader, signature: signature) as? Api.MessageReplyHeader + } } + var _12: [Api.MessageEntity]? + if Int(_1!) & Int(1 << 7) != 0 {if let _ = reader.readInt32() { + _12 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) + } } + var _13: Int32? + if Int(_1!) & Int(1 << 25) != 0 {_13 = 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.AuthorizationForm.authorizationForm(flags: _1!, requiredTypes: _2!, values: _3!, errors: _4!, users: _5!, privacyPolicyUrl: _6) + let _c6 = _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + let _c9 = (Int(_1!) & Int(1 << 2) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 11) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 3) == 0) || _11 != nil + let _c12 = (Int(_1!) & Int(1 << 7) == 0) || _12 != nil + let _c13 = (Int(_1!) & Int(1 << 25) == 0) || _13 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 { + return Api.Updates.updateShortChatMessage(flags: _1!, id: _2!, fromId: _3!, chatId: _4!, message: _5!, pts: _6!, ptsCount: _7!, date: _8!, fwdFrom: _9, viaBotId: _10, replyTo: _11, entities: _12, ttlPeriod: _13) } else { return nil } } - - } -} -public extension Api.account { - enum Authorizations: TypeConstructorDescription { - case authorizations(authorizationTtlDays: Int32, authorizations: [Api.Authorization]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .authorizations(let authorizationTtlDays, let authorizations): - if boxed { - buffer.appendInt32(1275039392) - } - serializeInt32(authorizationTtlDays, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(authorizations.count)) - for item in authorizations { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .authorizations(let authorizationTtlDays, let authorizations): - return ("authorizations", [("authorizationTtlDays", authorizationTtlDays as Any), ("authorizations", authorizations as Any)]) - } - } - - public static func parse_authorizations(_ reader: BufferReader) -> Authorizations? { + public static func parse_updateShortMessage(_ reader: BufferReader) -> Updates? { var _1: Int32? _1 = reader.readInt32() - var _2: [Api.Authorization]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Authorization.self) + var _2: Int32? + _2 = reader.readInt32() + var _3: Int64? + _3 = reader.readInt64() + var _4: String? + _4 = parseString(reader) + var _5: Int32? + _5 = reader.readInt32() + var _6: Int32? + _6 = reader.readInt32() + var _7: Int32? + _7 = reader.readInt32() + var _8: Api.MessageFwdHeader? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.MessageFwdHeader + } } + var _9: Int64? + if Int(_1!) & Int(1 << 11) != 0 {_9 = reader.readInt64() } + var _10: Api.MessageReplyHeader? + if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { + _10 = Api.parse(reader, signature: signature) as? Api.MessageReplyHeader + } } + var _11: [Api.MessageEntity]? + if Int(_1!) & Int(1 << 7) != 0 {if let _ = reader.readInt32() { + _11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) + } } + var _12: Int32? + if Int(_1!) & Int(1 << 25) != 0 {_12 = 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 + let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil + let _c9 = (Int(_1!) & Int(1 << 11) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 3) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 7) == 0) || _11 != nil + let _c12 = (Int(_1!) & Int(1 << 25) == 0) || _12 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 { + return Api.Updates.updateShortMessage(flags: _1!, id: _2!, userId: _3!, message: _4!, pts: _5!, ptsCount: _6!, date: _7!, fwdFrom: _8, viaBotId: _9, replyTo: _10, entities: _11, ttlPeriod: _12) + } + else { + return nil } + } + public static func parse_updateShortSentMessage(_ reader: BufferReader) -> Updates? { + 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: Api.MessageMedia? + if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.MessageMedia + } } + var _7: [Api.MessageEntity]? + if Int(_1!) & Int(1 << 7) != 0 {if let _ = reader.readInt32() { + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) + } } + var _8: Int32? + if Int(_1!) & Int(1 << 25) != 0 {_8 = reader.readInt32() } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.Authorizations.authorizations(authorizationTtlDays: _1!, authorizations: _2!) + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = (Int(_1!) & Int(1 << 9) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 7) == 0) || _7 != nil + let _c8 = (Int(_1!) & Int(1 << 25) == 0) || _8 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { + return Api.Updates.updateShortSentMessage(flags: _1!, id: _2!, pts: _3!, ptsCount: _4!, date: _5!, media: _6, entities: _7, ttlPeriod: _8) } else { return nil } } - - } -} -public extension Api.account { - enum AutoDownloadSettings: TypeConstructorDescription { - case autoDownloadSettings(low: Api.AutoDownloadSettings, medium: Api.AutoDownloadSettings, high: Api.AutoDownloadSettings) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .autoDownloadSettings(let low, let medium, let high): - if boxed { - buffer.appendInt32(1674235686) - } - low.serialize(buffer, true) - medium.serialize(buffer, true) - high.serialize(buffer, true) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .autoDownloadSettings(let low, let medium, let high): - return ("autoDownloadSettings", [("low", low as Any), ("medium", medium as Any), ("high", high as Any)]) - } - } - - public static func parse_autoDownloadSettings(_ reader: BufferReader) -> AutoDownloadSettings? { - var _1: Api.AutoDownloadSettings? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.AutoDownloadSettings + public static func parse_updates(_ reader: BufferReader) -> Updates? { + var _1: [Api.Update]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Update.self) } - var _2: Api.AutoDownloadSettings? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.AutoDownloadSettings + var _2: [Api.User]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } - var _3: Api.AutoDownloadSettings? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.AutoDownloadSettings + var _3: [Api.Chat]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) } + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.account.AutoDownloadSettings.autoDownloadSettings(low: _1!, medium: _2!, high: _3!) + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.Updates.updates(updates: _1!, users: _2!, chats: _3!, date: _4!, seq: _5!) } else { return nil } } - - } -} -public extension Api.account { - enum AutoSaveSettings: TypeConstructorDescription { - case autoSaveSettings(usersSettings: Api.AutoSaveSettings, chatsSettings: Api.AutoSaveSettings, broadcastsSettings: Api.AutoSaveSettings, exceptions: [Api.AutoSaveException], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .autoSaveSettings(let usersSettings, let chatsSettings, let broadcastsSettings, let exceptions, let chats, let users): - if boxed { - buffer.appendInt32(1279133341) - } - usersSettings.serialize(buffer, true) - chatsSettings.serialize(buffer, true) - broadcastsSettings.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(exceptions.count)) - for item in exceptions { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .autoSaveSettings(let usersSettings, let chatsSettings, let broadcastsSettings, let exceptions, let chats, let users): - return ("autoSaveSettings", [("usersSettings", usersSettings as Any), ("chatsSettings", chatsSettings as Any), ("broadcastsSettings", broadcastsSettings as Any), ("exceptions", exceptions as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_autoSaveSettings(_ reader: BufferReader) -> AutoSaveSettings? { - var _1: Api.AutoSaveSettings? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.AutoSaveSettings - } - var _2: Api.AutoSaveSettings? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.AutoSaveSettings - } - var _3: Api.AutoSaveSettings? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.AutoSaveSettings - } - var _4: [Api.AutoSaveException]? + public static func parse_updatesCombined(_ reader: BufferReader) -> Updates? { + var _1: [Api.Update]? if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.AutoSaveException.self) + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Update.self) } - var _5: [Api.Chat]? + var _2: [Api.User]? if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } - var _6: [Api.User]? + var _3: [Api.Chat]? if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() + var _6: Int32? + _6 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -415,105 +360,88 @@ public extension Api.account { let _c5 = _5 != nil let _c6 = _6 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.account.AutoSaveSettings.autoSaveSettings(usersSettings: _1!, chatsSettings: _2!, broadcastsSettings: _3!, exceptions: _4!, chats: _5!, users: _6!) + return Api.Updates.updatesCombined(updates: _1!, users: _2!, chats: _3!, date: _4!, seqStart: _5!, seq: _6!) } else { return nil } } + public static func parse_updatesTooLong(_ reader: BufferReader) -> Updates? { + return Api.Updates.updatesTooLong + } } } -public extension Api.account { - enum ContentSettings: TypeConstructorDescription { - case contentSettings(flags: Int32) +public extension Api { + enum UrlAuthResult: TypeConstructorDescription { + case urlAuthResultAccepted(url: String) + case urlAuthResultDefault + case urlAuthResultRequest(flags: Int32, bot: Api.User, domain: String) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .contentSettings(let flags): + case .urlAuthResultAccepted(let url): if boxed { - buffer.appendInt32(1474462241) + buffer.appendInt32(-1886646706) } - serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(url, buffer: buffer, boxed: false) break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .contentSettings(let flags): - return ("contentSettings", [("flags", flags as Any)]) - } - } - - public static func parse_contentSettings(_ reader: BufferReader) -> ContentSettings? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.account.ContentSettings.contentSettings(flags: _1!) - } - else { - return nil - } - } - - } -} -public extension Api.account { - enum EmailVerified: TypeConstructorDescription { - case emailVerified(email: String) - case emailVerifiedLogin(email: String, sentCode: Api.auth.SentCode) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .emailVerified(let email): + case .urlAuthResultDefault: if boxed { - buffer.appendInt32(731303195) + buffer.appendInt32(-1445536993) } - serializeString(email, buffer: buffer, boxed: false) + break - case .emailVerifiedLogin(let email, let sentCode): + case .urlAuthResultRequest(let flags, let bot, let domain): if boxed { - buffer.appendInt32(-507835039) + buffer.appendInt32(-1831650802) } - serializeString(email, buffer: buffer, boxed: false) - sentCode.serialize(buffer, true) + serializeInt32(flags, buffer: buffer, boxed: false) + bot.serialize(buffer, true) + serializeString(domain, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .emailVerified(let email): - return ("emailVerified", [("email", email as Any)]) - case .emailVerifiedLogin(let email, let sentCode): - return ("emailVerifiedLogin", [("email", email as Any), ("sentCode", sentCode as Any)]) + case .urlAuthResultAccepted(let url): + return ("urlAuthResultAccepted", [("url", url as Any)]) + case .urlAuthResultDefault: + return ("urlAuthResultDefault", []) + case .urlAuthResultRequest(let flags, let bot, let domain): + return ("urlAuthResultRequest", [("flags", flags as Any), ("bot", bot as Any), ("domain", domain as Any)]) } } - public static func parse_emailVerified(_ reader: BufferReader) -> EmailVerified? { + public static func parse_urlAuthResultAccepted(_ reader: BufferReader) -> UrlAuthResult? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil if _c1 { - return Api.account.EmailVerified.emailVerified(email: _1!) + return Api.UrlAuthResult.urlAuthResultAccepted(url: _1!) } else { return nil } } - public static func parse_emailVerifiedLogin(_ reader: BufferReader) -> EmailVerified? { - var _1: String? - _1 = parseString(reader) - var _2: Api.auth.SentCode? + public static func parse_urlAuthResultDefault(_ reader: BufferReader) -> UrlAuthResult? { + return Api.UrlAuthResult.urlAuthResultDefault + } + public static func parse_urlAuthResultRequest(_ reader: BufferReader) -> UrlAuthResult? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.User? if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.auth.SentCode + _2 = Api.parse(reader, signature: signature) as? Api.User } + var _3: String? + _3 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.EmailVerified.emailVerifiedLogin(email: _1!, sentCode: _2!) + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.UrlAuthResult.urlAuthResultRequest(flags: _1!, bot: _2!, domain: _3!) } else { return nil @@ -522,138 +450,148 @@ public extension Api.account { } } -public extension Api.account { - enum EmojiStatuses: TypeConstructorDescription { - case emojiStatuses(hash: Int64, statuses: [Api.EmojiStatus]) - case emojiStatusesNotModified +public extension Api { + enum User: TypeConstructorDescription { + case user(flags: Int32, flags2: Int32, id: Int64, accessHash: Int64?, firstName: String?, lastName: String?, username: String?, phone: String?, photo: Api.UserProfilePhoto?, status: Api.UserStatus?, botInfoVersion: Int32?, restrictionReason: [Api.RestrictionReason]?, botInlinePlaceholder: String?, langCode: String?, emojiStatus: Api.EmojiStatus?, usernames: [Api.Username]?, storiesMaxId: Int32?, color: Api.PeerColor?, profileColor: Api.PeerColor?) + case userEmpty(id: Int64) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .emojiStatuses(let hash, let statuses): + case .user(let flags, let flags2, let id, let accessHash, let firstName, let lastName, let username, let phone, let photo, let status, let botInfoVersion, let restrictionReason, let botInlinePlaceholder, let langCode, let emojiStatus, let usernames, let storiesMaxId, let color, let profileColor): if boxed { - buffer.appendInt32(-1866176559) + buffer.appendInt32(559694904) } - serializeInt64(hash, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(statuses.count)) - for item in statuses { + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(flags2, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt64(accessHash!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(firstName!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(lastName!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {serializeString(username!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {serializeString(phone!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 5) != 0 {photo!.serialize(buffer, true)} + if Int(flags) & Int(1 << 6) != 0 {status!.serialize(buffer, true)} + if Int(flags) & Int(1 << 14) != 0 {serializeInt32(botInfoVersion!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 18) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(restrictionReason!.count)) + for item in restrictionReason! { item.serialize(buffer, true) - } - break - case .emojiStatusesNotModified: - if boxed { - buffer.appendInt32(-796072379) - } - + }} + if Int(flags) & Int(1 << 19) != 0 {serializeString(botInlinePlaceholder!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 22) != 0 {serializeString(langCode!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 30) != 0 {emojiStatus!.serialize(buffer, true)} + if Int(flags2) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(usernames!.count)) + for item in usernames! { + item.serialize(buffer, true) + }} + if Int(flags2) & Int(1 << 5) != 0 {serializeInt32(storiesMaxId!, buffer: buffer, boxed: false)} + if Int(flags2) & Int(1 << 8) != 0 {color!.serialize(buffer, true)} + if Int(flags2) & Int(1 << 9) != 0 {profileColor!.serialize(buffer, true)} break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .emojiStatuses(let hash, let statuses): - return ("emojiStatuses", [("hash", hash as Any), ("statuses", statuses as Any)]) - case .emojiStatusesNotModified: - return ("emojiStatusesNotModified", []) - } - } - - public static func parse_emojiStatuses(_ reader: BufferReader) -> EmojiStatuses? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.EmojiStatus]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.EmojiStatus.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.EmojiStatuses.emojiStatuses(hash: _1!, statuses: _2!) - } - else { - return nil - } - } - public static func parse_emojiStatusesNotModified(_ reader: BufferReader) -> EmojiStatuses? { - return Api.account.EmojiStatuses.emojiStatusesNotModified - } - - } -} -public extension Api.account { - enum Password: TypeConstructorDescription { - case password(flags: Int32, currentAlgo: Api.PasswordKdfAlgo?, srpB: Buffer?, srpId: Int64?, hint: String?, emailUnconfirmedPattern: String?, newAlgo: Api.PasswordKdfAlgo, newSecureAlgo: Api.SecurePasswordKdfAlgo, secureRandom: Buffer, pendingResetDate: Int32?, loginEmailPattern: String?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .password(let flags, let currentAlgo, let srpB, let srpId, let hint, let emailUnconfirmedPattern, let newAlgo, let newSecureAlgo, let secureRandom, let pendingResetDate, let loginEmailPattern): + case .userEmpty(let id): if boxed { - buffer.appendInt32(-1787080453) + buffer.appendInt32(-742634630) } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {currentAlgo!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {serializeBytes(srpB!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeInt64(srpId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {serializeString(hint!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {serializeString(emailUnconfirmedPattern!, buffer: buffer, boxed: false)} - newAlgo.serialize(buffer, true) - newSecureAlgo.serialize(buffer, true) - serializeBytes(secureRandom, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 5) != 0 {serializeInt32(pendingResetDate!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 6) != 0 {serializeString(loginEmailPattern!, buffer: buffer, boxed: false)} + serializeInt64(id, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .password(let flags, let currentAlgo, let srpB, let srpId, let hint, let emailUnconfirmedPattern, let newAlgo, let newSecureAlgo, let secureRandom, let pendingResetDate, let loginEmailPattern): - return ("password", [("flags", flags as Any), ("currentAlgo", currentAlgo as Any), ("srpB", srpB as Any), ("srpId", srpId as Any), ("hint", hint as Any), ("emailUnconfirmedPattern", emailUnconfirmedPattern as Any), ("newAlgo", newAlgo as Any), ("newSecureAlgo", newSecureAlgo as Any), ("secureRandom", secureRandom as Any), ("pendingResetDate", pendingResetDate as Any), ("loginEmailPattern", loginEmailPattern as Any)]) + case .user(let flags, let flags2, let id, let accessHash, let firstName, let lastName, let username, let phone, let photo, let status, let botInfoVersion, let restrictionReason, let botInlinePlaceholder, let langCode, let emojiStatus, let usernames, let storiesMaxId, let color, let profileColor): + return ("user", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("firstName", firstName as Any), ("lastName", lastName as Any), ("username", username as Any), ("phone", phone as Any), ("photo", photo as Any), ("status", status as Any), ("botInfoVersion", botInfoVersion as Any), ("restrictionReason", restrictionReason as Any), ("botInlinePlaceholder", botInlinePlaceholder as Any), ("langCode", langCode as Any), ("emojiStatus", emojiStatus as Any), ("usernames", usernames as Any), ("storiesMaxId", storiesMaxId as Any), ("color", color as Any), ("profileColor", profileColor as Any)]) + case .userEmpty(let id): + return ("userEmpty", [("id", id as Any)]) } } - public static func parse_password(_ reader: BufferReader) -> Password? { + public static func parse_user(_ reader: BufferReader) -> User? { var _1: Int32? _1 = reader.readInt32() - var _2: Api.PasswordKdfAlgo? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.PasswordKdfAlgo - } } - var _3: Buffer? - if Int(_1!) & Int(1 << 2) != 0 {_3 = parseBytes(reader) } + var _2: Int32? + _2 = reader.readInt32() + var _3: Int64? + _3 = reader.readInt64() var _4: Int64? - if Int(_1!) & Int(1 << 2) != 0 {_4 = reader.readInt64() } + if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt64() } var _5: String? - if Int(_1!) & Int(1 << 3) != 0 {_5 = parseString(reader) } + if Int(_1!) & Int(1 << 1) != 0 {_5 = parseString(reader) } var _6: String? - if Int(_1!) & Int(1 << 4) != 0 {_6 = parseString(reader) } - var _7: Api.PasswordKdfAlgo? - if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.PasswordKdfAlgo + if Int(_1!) & Int(1 << 2) != 0 {_6 = parseString(reader) } + var _7: String? + if Int(_1!) & Int(1 << 3) != 0 {_7 = parseString(reader) } + var _8: String? + if Int(_1!) & Int(1 << 4) != 0 {_8 = parseString(reader) } + var _9: Api.UserProfilePhoto? + if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.UserProfilePhoto + } } + var _10: Api.UserStatus? + if Int(_1!) & Int(1 << 6) != 0 {if let signature = reader.readInt32() { + _10 = Api.parse(reader, signature: signature) as? Api.UserStatus + } } + var _11: Int32? + if Int(_1!) & Int(1 << 14) != 0 {_11 = reader.readInt32() } + var _12: [Api.RestrictionReason]? + if Int(_1!) & Int(1 << 18) != 0 {if let _ = reader.readInt32() { + _12 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self) + } } + var _13: String? + if Int(_1!) & Int(1 << 19) != 0 {_13 = parseString(reader) } + var _14: String? + if Int(_1!) & Int(1 << 22) != 0 {_14 = parseString(reader) } + var _15: Api.EmojiStatus? + if Int(_1!) & Int(1 << 30) != 0 {if let signature = reader.readInt32() { + _15 = Api.parse(reader, signature: signature) as? Api.EmojiStatus + } } + var _16: [Api.Username]? + if Int(_2!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { + _16 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Username.self) + } } + var _17: Int32? + if Int(_2!) & Int(1 << 5) != 0 {_17 = reader.readInt32() } + var _18: Api.PeerColor? + if Int(_2!) & Int(1 << 8) != 0 {if let signature = reader.readInt32() { + _18 = Api.parse(reader, signature: signature) as? Api.PeerColor + } } + var _19: Api.PeerColor? + if Int(_2!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() { + _19 = Api.parse(reader, signature: signature) as? Api.PeerColor + } } + 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 + 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 << 4) == 0) || _8 != nil + let _c9 = (Int(_1!) & Int(1 << 5) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 6) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 14) == 0) || _11 != nil + let _c12 = (Int(_1!) & Int(1 << 18) == 0) || _12 != nil + let _c13 = (Int(_1!) & Int(1 << 19) == 0) || _13 != nil + let _c14 = (Int(_1!) & Int(1 << 22) == 0) || _14 != nil + let _c15 = (Int(_1!) & Int(1 << 30) == 0) || _15 != nil + let _c16 = (Int(_2!) & Int(1 << 0) == 0) || _16 != nil + let _c17 = (Int(_2!) & Int(1 << 5) == 0) || _17 != nil + let _c18 = (Int(_2!) & Int(1 << 8) == 0) || _18 != nil + let _c19 = (Int(_2!) & Int(1 << 9) == 0) || _19 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 { + return Api.User.user(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, firstName: _5, lastName: _6, username: _7, phone: _8, photo: _9, status: _10, botInfoVersion: _11, restrictionReason: _12, botInlinePlaceholder: _13, langCode: _14, emojiStatus: _15, usernames: _16, storiesMaxId: _17, color: _18, profileColor: _19) } - var _8: Api.SecurePasswordKdfAlgo? - if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.SecurePasswordKdfAlgo + else { + return nil } - var _9: Buffer? - _9 = parseBytes(reader) - var _10: Int32? - if Int(_1!) & Int(1 << 5) != 0 {_10 = reader.readInt32() } - var _11: String? - if Int(_1!) & Int(1 << 6) != 0 {_11 = parseString(reader) } + } + public static func parse_userEmpty(_ reader: BufferReader) -> User? { + var _1: Int64? + _1 = reader.readInt64() let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 2) == 0) || _2 != nil - let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil - let _c6 = (Int(_1!) & Int(1 << 4) == 0) || _6 != nil - let _c7 = _7 != nil - let _c8 = _8 != nil - let _c9 = _9 != nil - let _c10 = (Int(_1!) & Int(1 << 5) == 0) || _10 != nil - let _c11 = (Int(_1!) & Int(1 << 6) == 0) || _11 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { - return Api.account.Password.password(flags: _1!, currentAlgo: _2, srpB: _3, srpId: _4, hint: _5, emailUnconfirmedPattern: _6, newAlgo: _7!, newSecureAlgo: _8!, secureRandom: _9!, pendingResetDate: _10, loginEmailPattern: _11) + if _c1 { + return Api.User.userEmpty(id: _1!) } else { return nil @@ -662,58 +600,136 @@ public extension Api.account { } } -public extension Api.account { - enum PasswordInputSettings: TypeConstructorDescription { - case passwordInputSettings(flags: Int32, newAlgo: Api.PasswordKdfAlgo?, newPasswordHash: Buffer?, hint: String?, email: String?, newSecureSettings: Api.SecureSecretSettings?) +public extension Api { + enum UserFull: TypeConstructorDescription { + case userFull(flags: 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?, premiumGifts: [Api.PremiumGiftOption]?, wallpaper: Api.WallPaper?, stories: Api.PeerStories?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .passwordInputSettings(let flags, let newAlgo, let newPasswordHash, let hint, let email, let newSecureSettings): + case .userFull(let flags, 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 premiumGifts, let wallpaper, let stories): if boxed { - buffer.appendInt32(-1036572727) + buffer.appendInt32(-1179571092) } serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {newAlgo!.serialize(buffer, true)} - if Int(flags) & Int(1 << 0) != 0 {serializeBytes(newPasswordHash!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 0) != 0 {serializeString(hint!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeString(email!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {newSecureSettings!.serialize(buffer, true)} + serializeInt64(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeString(about!, buffer: buffer, boxed: false)} + settings.serialize(buffer, true) + if Int(flags) & Int(1 << 21) != 0 {personalPhoto!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {profilePhoto!.serialize(buffer, true)} + if Int(flags) & Int(1 << 22) != 0 {fallbackPhoto!.serialize(buffer, true)} + notifySettings.serialize(buffer, true) + if Int(flags) & Int(1 << 3) != 0 {botInfo!.serialize(buffer, true)} + if Int(flags) & Int(1 << 6) != 0 {serializeInt32(pinnedMsgId!, buffer: buffer, boxed: false)} + 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 << 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)} + if Int(flags) & Int(1 << 19) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(premiumGifts!.count)) + for item in premiumGifts! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 24) != 0 {wallpaper!.serialize(buffer, true)} + if Int(flags) & Int(1 << 25) != 0 {stories!.serialize(buffer, true)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .passwordInputSettings(let flags, let newAlgo, let newPasswordHash, let hint, let email, let newSecureSettings): - return ("passwordInputSettings", [("flags", flags as Any), ("newAlgo", newAlgo as Any), ("newPasswordHash", newPasswordHash as Any), ("hint", hint as Any), ("email", email as Any), ("newSecureSettings", newSecureSettings as Any)]) + case .userFull(let flags, 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 premiumGifts, let wallpaper, let stories): + return ("userFull", [("flags", flags 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), ("premiumGifts", premiumGifts as Any), ("wallpaper", wallpaper as Any), ("stories", stories as Any)]) } } - public static func parse_passwordInputSettings(_ reader: BufferReader) -> PasswordInputSettings? { + public static func parse_userFull(_ reader: BufferReader) -> UserFull? { var _1: Int32? _1 = reader.readInt32() - var _2: Api.PasswordKdfAlgo? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.PasswordKdfAlgo + var _2: Int64? + _2 = reader.readInt64() + var _3: String? + if Int(_1!) & Int(1 << 1) != 0 {_3 = parseString(reader) } + var _4: Api.PeerSettings? + if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.PeerSettings + } + var _5: Api.Photo? + if Int(_1!) & Int(1 << 21) != 0 {if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.Photo } } - var _3: Buffer? - if Int(_1!) & Int(1 << 0) != 0 {_3 = parseBytes(reader) } - 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) } - var _6: Api.SecureSecretSettings? + var _6: Api.Photo? if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.SecureSecretSettings + _6 = Api.parse(reader, signature: signature) as? Api.Photo + } } + var _7: Api.Photo? + if Int(_1!) & Int(1 << 22) != 0 {if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.Photo + } } + var _8: Api.PeerNotifySettings? + if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings + } + var _9: Api.BotInfo? + if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.BotInfo + } } + var _10: Int32? + if Int(_1!) & Int(1 << 6) != 0 {_10 = reader.readInt32() } + var _11: Int32? + _11 = reader.readInt32() + var _12: Int32? + if Int(_1!) & Int(1 << 11) != 0 {_12 = reader.readInt32() } + var _13: Int32? + if Int(_1!) & Int(1 << 14) != 0 {_13 = reader.readInt32() } + var _14: String? + if Int(_1!) & Int(1 << 15) != 0 {_14 = parseString(reader) } + var _15: String? + if Int(_1!) & Int(1 << 16) != 0 {_15 = parseString(reader) } + var _16: Api.ChatAdminRights? + if Int(_1!) & Int(1 << 17) != 0 {if let signature = reader.readInt32() { + _16 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights + } } + var _17: Api.ChatAdminRights? + if Int(_1!) & Int(1 << 18) != 0 {if let signature = reader.readInt32() { + _17 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights + } } + var _18: [Api.PremiumGiftOption]? + if Int(_1!) & Int(1 << 19) != 0 {if let _ = reader.readInt32() { + _18 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PremiumGiftOption.self) + } } + var _19: Api.WallPaper? + if Int(_1!) & Int(1 << 24) != 0 {if let signature = reader.readInt32() { + _19 = Api.parse(reader, signature: signature) as? Api.WallPaper + } } + var _20: Api.PeerStories? + if Int(_1!) & Int(1 << 25) != 0 {if let signature = reader.readInt32() { + _20 = Api.parse(reader, signature: signature) as? Api.PeerStories } } let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c4 = _4 != nil + let _c5 = (Int(_1!) & Int(1 << 21) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.account.PasswordInputSettings.passwordInputSettings(flags: _1!, newAlgo: _2, newPasswordHash: _3, hint: _4, email: _5, newSecureSettings: _6) + let _c7 = (Int(_1!) & Int(1 << 22) == 0) || _7 != nil + let _c8 = _8 != nil + let _c9 = (Int(_1!) & Int(1 << 3) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 6) == 0) || _10 != nil + let _c11 = _11 != nil + let _c12 = (Int(_1!) & Int(1 << 11) == 0) || _12 != nil + let _c13 = (Int(_1!) & Int(1 << 14) == 0) || _13 != nil + let _c14 = (Int(_1!) & Int(1 << 15) == 0) || _14 != nil + let _c15 = (Int(_1!) & Int(1 << 16) == 0) || _15 != nil + let _c16 = (Int(_1!) & Int(1 << 17) == 0) || _16 != nil + let _c17 = (Int(_1!) & Int(1 << 18) == 0) || _17 != nil + let _c18 = (Int(_1!) & Int(1 << 19) == 0) || _18 != nil + let _c19 = (Int(_1!) & Int(1 << 24) == 0) || _19 != nil + let _c20 = (Int(_1!) & Int(1 << 25) == 0) || _20 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 { + return Api.UserFull.userFull(flags: _1!, id: _2!, about: _3, settings: _4!, personalPhoto: _5, profilePhoto: _6, fallbackPhoto: _7, notifySettings: _8!, botInfo: _9, pinnedMsgId: _10, commonChatsCount: _11!, folderId: _12, ttlPeriod: _13, themeEmoticon: _14, privateForwardName: _15, botGroupAdminRights: _16, botBroadcastAdminRights: _17, premiumGifts: _18, wallpaper: _19, stories: _20) } else { return nil @@ -722,224 +738,202 @@ public extension Api.account { } } -public extension Api.account { - enum PasswordSettings: TypeConstructorDescription { - case passwordSettings(flags: Int32, email: String?, secureSettings: Api.SecureSecretSettings?) +public extension Api { + enum UserProfilePhoto: TypeConstructorDescription { + case userProfilePhoto(flags: Int32, photoId: Int64, strippedThumb: Buffer?, dcId: Int32) + case userProfilePhotoEmpty public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .passwordSettings(let flags, let email, let secureSettings): + case .userProfilePhoto(let flags, let photoId, let strippedThumb, let dcId): if boxed { - buffer.appendInt32(-1705233435) + buffer.appendInt32(-2100168954) } serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(email!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {secureSettings!.serialize(buffer, true)} + serializeInt64(photoId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeBytes(strippedThumb!, buffer: buffer, boxed: false)} + serializeInt32(dcId, buffer: buffer, boxed: false) + break + case .userProfilePhotoEmpty: + if boxed { + buffer.appendInt32(1326562017) + } + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .passwordSettings(let flags, let email, let secureSettings): - return ("passwordSettings", [("flags", flags as Any), ("email", email as Any), ("secureSettings", secureSettings as Any)]) + case .userProfilePhoto(let flags, let photoId, let strippedThumb, let dcId): + return ("userProfilePhoto", [("flags", flags as Any), ("photoId", photoId as Any), ("strippedThumb", strippedThumb as Any), ("dcId", dcId as Any)]) + case .userProfilePhotoEmpty: + return ("userProfilePhotoEmpty", []) } } - public static func parse_passwordSettings(_ reader: BufferReader) -> PasswordSettings? { + public static func parse_userProfilePhoto(_ reader: BufferReader) -> UserProfilePhoto? { var _1: Int32? _1 = reader.readInt32() - var _2: String? - if Int(_1!) & Int(1 << 0) != 0 {_2 = parseString(reader) } - var _3: Api.SecureSecretSettings? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.SecureSecretSettings - } } + var _2: Int64? + _2 = reader.readInt64() + var _3: Buffer? + if Int(_1!) & Int(1 << 1) != 0 {_3 = parseBytes(reader) } + var _4: Int32? + _4 = reader.readInt32() let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.account.PasswordSettings.passwordSettings(flags: _1!, email: _2, secureSettings: _3) + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.UserProfilePhoto.userProfilePhoto(flags: _1!, photoId: _2!, strippedThumb: _3, dcId: _4!) } else { return nil } } + public static func parse_userProfilePhotoEmpty(_ reader: BufferReader) -> UserProfilePhoto? { + return Api.UserProfilePhoto.userProfilePhotoEmpty + } } } -public extension Api.account { - enum PrivacyRules: TypeConstructorDescription { - case privacyRules(rules: [Api.PrivacyRule], chats: [Api.Chat], users: [Api.User]) +public extension Api { + enum UserStatus: TypeConstructorDescription { + case userStatusEmpty + case userStatusLastMonth + case userStatusLastWeek + case userStatusOffline(wasOnline: Int32) + case userStatusOnline(expires: Int32) + case userStatusRecently public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .privacyRules(let rules, let chats, let users): + case .userStatusEmpty: if boxed { - buffer.appendInt32(1352683077) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(rules.count)) - for item in rules { - item.serialize(buffer, true) + buffer.appendInt32(164646985) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) + + break + case .userStatusLastMonth: + if boxed { + buffer.appendInt32(2011940674) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + + break + case .userStatusLastWeek: + if boxed { + buffer.appendInt32(129960444) } + break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .privacyRules(let rules, let chats, let users): - return ("privacyRules", [("rules", rules as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_privacyRules(_ reader: BufferReader) -> PrivacyRules? { - var _1: [Api.PrivacyRule]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PrivacyRule.self) - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.account.PrivacyRules.privacyRules(rules: _1!, chats: _2!, users: _3!) - } - else { - return nil - } - } - - } -} -public extension Api.account { - enum ResetPasswordResult: TypeConstructorDescription { - case resetPasswordFailedWait(retryDate: Int32) - case resetPasswordOk - case resetPasswordRequestedWait(untilDate: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .resetPasswordFailedWait(let retryDate): + case .userStatusOffline(let wasOnline): if boxed { - buffer.appendInt32(-478701471) + buffer.appendInt32(9203775) } - serializeInt32(retryDate, buffer: buffer, boxed: false) + serializeInt32(wasOnline, buffer: buffer, boxed: false) break - case .resetPasswordOk: + case .userStatusOnline(let expires): if boxed { - buffer.appendInt32(-383330754) + buffer.appendInt32(-306628279) } - + serializeInt32(expires, buffer: buffer, boxed: false) break - case .resetPasswordRequestedWait(let untilDate): + case .userStatusRecently: if boxed { - buffer.appendInt32(-370148227) + buffer.appendInt32(-496024847) } - serializeInt32(untilDate, buffer: buffer, boxed: false) + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .resetPasswordFailedWait(let retryDate): - return ("resetPasswordFailedWait", [("retryDate", retryDate as Any)]) - case .resetPasswordOk: - return ("resetPasswordOk", []) - case .resetPasswordRequestedWait(let untilDate): - return ("resetPasswordRequestedWait", [("untilDate", untilDate as Any)]) - } - } - - public static func parse_resetPasswordFailedWait(_ reader: BufferReader) -> ResetPasswordResult? { + case .userStatusEmpty: + return ("userStatusEmpty", []) + case .userStatusLastMonth: + return ("userStatusLastMonth", []) + case .userStatusLastWeek: + return ("userStatusLastWeek", []) + case .userStatusOffline(let wasOnline): + return ("userStatusOffline", [("wasOnline", wasOnline as Any)]) + case .userStatusOnline(let expires): + return ("userStatusOnline", [("expires", expires as Any)]) + case .userStatusRecently: + return ("userStatusRecently", []) + } + } + + public static func parse_userStatusEmpty(_ reader: BufferReader) -> UserStatus? { + return Api.UserStatus.userStatusEmpty + } + public static func parse_userStatusLastMonth(_ reader: BufferReader) -> UserStatus? { + return Api.UserStatus.userStatusLastMonth + } + public static func parse_userStatusLastWeek(_ reader: BufferReader) -> UserStatus? { + return Api.UserStatus.userStatusLastWeek + } + public static func parse_userStatusOffline(_ reader: BufferReader) -> UserStatus? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil if _c1 { - return Api.account.ResetPasswordResult.resetPasswordFailedWait(retryDate: _1!) + return Api.UserStatus.userStatusOffline(wasOnline: _1!) } else { return nil } } - public static func parse_resetPasswordOk(_ reader: BufferReader) -> ResetPasswordResult? { - return Api.account.ResetPasswordResult.resetPasswordOk - } - public static func parse_resetPasswordRequestedWait(_ reader: BufferReader) -> ResetPasswordResult? { + public static func parse_userStatusOnline(_ reader: BufferReader) -> UserStatus? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil if _c1 { - return Api.account.ResetPasswordResult.resetPasswordRequestedWait(untilDate: _1!) + return Api.UserStatus.userStatusOnline(expires: _1!) } else { return nil } } + public static func parse_userStatusRecently(_ reader: BufferReader) -> UserStatus? { + return Api.UserStatus.userStatusRecently + } } } -public extension Api.account { - enum SavedRingtone: TypeConstructorDescription { - case savedRingtone - case savedRingtoneConverted(document: Api.Document) +public extension Api { + enum Username: TypeConstructorDescription { + case username(flags: Int32, username: String) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .savedRingtone: - if boxed { - buffer.appendInt32(-1222230163) - } - - break - case .savedRingtoneConverted(let document): + case .username(let flags, let username): if boxed { - buffer.appendInt32(523271863) + buffer.appendInt32(-1274595769) } - document.serialize(buffer, true) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(username, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .savedRingtone: - return ("savedRingtone", []) - case .savedRingtoneConverted(let document): - return ("savedRingtoneConverted", [("document", document as Any)]) + case .username(let flags, let username): + return ("username", [("flags", flags as Any), ("username", username as Any)]) } } - public static func parse_savedRingtone(_ reader: BufferReader) -> SavedRingtone? { - return Api.account.SavedRingtone.savedRingtone - } - public static func parse_savedRingtoneConverted(_ reader: BufferReader) -> SavedRingtone? { - var _1: Api.Document? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Document - } + public static func parse_username(_ reader: BufferReader) -> Username? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.account.SavedRingtone.savedRingtoneConverted(document: _1!) + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Username.username(flags: _1!, username: _2!) } else { return nil @@ -948,132 +942,210 @@ public extension Api.account { } } -public extension Api.account { - enum SavedRingtones: TypeConstructorDescription { - case savedRingtones(hash: Int64, ringtones: [Api.Document]) - case savedRingtonesNotModified +public extension Api { + enum VideoSize: TypeConstructorDescription { + case videoSize(flags: Int32, type: String, w: Int32, h: Int32, size: Int32, videoStartTs: Double?) + case videoSizeEmojiMarkup(emojiId: Int64, backgroundColors: [Int32]) + case videoSizeStickerMarkup(stickerset: Api.InputStickerSet, stickerId: Int64, backgroundColors: [Int32]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .savedRingtones(let hash, let ringtones): + case .videoSize(let flags, let type, let w, let h, let size, let videoStartTs): if boxed { - buffer.appendInt32(-1041683259) + buffer.appendInt32(-567037804) } - serializeInt64(hash, buffer: buffer, boxed: false) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(type, buffer: buffer, boxed: false) + serializeInt32(w, buffer: buffer, boxed: false) + serializeInt32(h, buffer: buffer, boxed: false) + serializeInt32(size, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeDouble(videoStartTs!, buffer: buffer, boxed: false)} + break + case .videoSizeEmojiMarkup(let emojiId, let backgroundColors): + if boxed { + buffer.appendInt32(-128171716) + } + serializeInt64(emojiId, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(ringtones.count)) - for item in ringtones { - item.serialize(buffer, true) + buffer.appendInt32(Int32(backgroundColors.count)) + for item in backgroundColors { + serializeInt32(item, buffer: buffer, boxed: false) } break - case .savedRingtonesNotModified: + case .videoSizeStickerMarkup(let stickerset, let stickerId, let backgroundColors): if boxed { - buffer.appendInt32(-67704655) + buffer.appendInt32(228623102) + } + stickerset.serialize(buffer, true) + serializeInt64(stickerId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(backgroundColors.count)) + for item in backgroundColors { + serializeInt32(item, buffer: buffer, boxed: false) } - 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", []) + case .videoSize(let flags, let type, let w, let h, let size, let videoStartTs): + return ("videoSize", [("flags", flags as Any), ("type", type as Any), ("w", w as Any), ("h", h as Any), ("size", size as Any), ("videoStartTs", videoStartTs as Any)]) + case .videoSizeEmojiMarkup(let emojiId, let backgroundColors): + return ("videoSizeEmojiMarkup", [("emojiId", emojiId as Any), ("backgroundColors", backgroundColors as Any)]) + case .videoSizeStickerMarkup(let stickerset, let stickerId, let backgroundColors): + return ("videoSizeStickerMarkup", [("stickerset", stickerset as Any), ("stickerId", stickerId as Any), ("backgroundColors", backgroundColors as Any)]) } } - public static func parse_savedRingtones(_ reader: BufferReader) -> SavedRingtones? { + public static func parse_videoSize(_ reader: BufferReader) -> VideoSize? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() + var _6: Double? + if Int(_1!) & Int(1 << 0) != 0 {_6 = reader.readDouble() } + 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.VideoSize.videoSize(flags: _1!, type: _2!, w: _3!, h: _4!, size: _5!, videoStartTs: _6) + } + else { + return nil + } + } + public static func parse_videoSizeEmojiMarkup(_ reader: BufferReader) -> VideoSize? { var _1: Int64? _1 = reader.readInt64() - var _2: [Api.Document]? + var _2: [Int32]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + _2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) } let _c1 = _1 != nil let _c2 = _2 != nil if _c1 && _c2 { - return Api.account.SavedRingtones.savedRingtones(hash: _1!, ringtones: _2!) + return Api.VideoSize.videoSizeEmojiMarkup(emojiId: _1!, backgroundColors: _2!) } else { return nil } } - public static func parse_savedRingtonesNotModified(_ reader: BufferReader) -> SavedRingtones? { - return Api.account.SavedRingtones.savedRingtonesNotModified + public static func parse_videoSizeStickerMarkup(_ reader: BufferReader) -> VideoSize? { + var _1: Api.InputStickerSet? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.InputStickerSet + } + var _2: Int64? + _2 = reader.readInt64() + var _3: [Int32]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.VideoSize.videoSizeStickerMarkup(stickerset: _1!, stickerId: _2!, backgroundColors: _3!) + } + else { + return nil + } } } } -public extension Api.account { - enum SentEmailCode: TypeConstructorDescription { - case sentEmailCode(emailPattern: String, length: Int32) +public extension Api { + enum WallPaper: TypeConstructorDescription { + case wallPaper(id: Int64, flags: Int32, accessHash: Int64, slug: String, document: Api.Document, settings: Api.WallPaperSettings?) + case wallPaperNoFile(id: Int64, flags: Int32, settings: Api.WallPaperSettings?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .sentEmailCode(let emailPattern, let length): + case .wallPaper(let id, let flags, let accessHash, let slug, let document, let settings): + if boxed { + buffer.appendInt32(-1539849235) + } + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + serializeString(slug, buffer: buffer, boxed: false) + document.serialize(buffer, true) + if Int(flags) & Int(1 << 2) != 0 {settings!.serialize(buffer, true)} + break + case .wallPaperNoFile(let id, let flags, let settings): if boxed { - buffer.appendInt32(-2128640689) + buffer.appendInt32(-528465642) } - serializeString(emailPattern, buffer: buffer, boxed: false) - serializeInt32(length, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {settings!.serialize(buffer, true)} 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)]) + case .wallPaper(let id, let flags, let accessHash, let slug, let document, let settings): + return ("wallPaper", [("id", id as Any), ("flags", flags as Any), ("accessHash", accessHash as Any), ("slug", slug as Any), ("document", document as Any), ("settings", settings as Any)]) + case .wallPaperNoFile(let id, let flags, let settings): + return ("wallPaperNoFile", [("id", id as Any), ("flags", flags as Any), ("settings", settings as Any)]) } } - public static func parse_sentEmailCode(_ reader: BufferReader) -> SentEmailCode? { - var _1: String? - _1 = parseString(reader) + public static func parse_wallPaper(_ reader: BufferReader) -> WallPaper? { + var _1: Int64? + _1 = reader.readInt64() var _2: Int32? _2 = reader.readInt32() + var _3: Int64? + _3 = reader.readInt64() + var _4: String? + _4 = parseString(reader) + var _5: Api.Document? + if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.Document + } + var _6: Api.WallPaperSettings? + if Int(_2!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.WallPaperSettings + } } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.SentEmailCode.sentEmailCode(emailPattern: _1!, length: _2!) + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = (Int(_2!) & Int(1 << 2) == 0) || _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.WallPaper.wallPaper(id: _1!, flags: _2!, accessHash: _3!, slug: _4!, document: _5!, settings: _6) } else { return nil } } - - } -} -public extension Api.account { - enum Takeout: TypeConstructorDescription { - case takeout(id: Int64) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .takeout(let id): - if boxed { - buffer.appendInt32(1304052993) - } - serializeInt64(id, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .takeout(let id): - return ("takeout", [("id", id as Any)]) - } - } - - public static func parse_takeout(_ reader: BufferReader) -> Takeout? { + public static func parse_wallPaperNoFile(_ reader: BufferReader) -> WallPaper? { var _1: Int64? _1 = reader.readInt64() + var _2: Int32? + _2 = reader.readInt32() + var _3: Api.WallPaperSettings? + if Int(_2!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.WallPaperSettings + } } let _c1 = _1 != nil - if _c1 { - return Api.account.Takeout.takeout(id: _1!) + let _c2 = _2 != nil + let _c3 = (Int(_2!) & Int(1 << 2) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.WallPaper.wallPaperNoFile(id: _1!, flags: _2!, settings: _3) } else { return nil @@ -1082,96 +1154,126 @@ public extension Api.account { } } -public extension Api.account { - enum Themes: TypeConstructorDescription { - case themes(hash: Int64, themes: [Api.Theme]) - case themesNotModified +public extension Api { + enum WallPaperSettings: TypeConstructorDescription { + case wallPaperSettings(flags: Int32, backgroundColor: Int32?, secondBackgroundColor: Int32?, thirdBackgroundColor: Int32?, fourthBackgroundColor: Int32?, intensity: Int32?, rotation: Int32?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .themes(let hash, let themes): - if boxed { - buffer.appendInt32(-1707242387) - } - serializeInt64(hash, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(themes.count)) - for item in themes { - item.serialize(buffer, true) - } - break - case .themesNotModified: + case .wallPaperSettings(let flags, let backgroundColor, let secondBackgroundColor, let thirdBackgroundColor, let fourthBackgroundColor, let intensity, let rotation): if boxed { - buffer.appendInt32(-199313886) + buffer.appendInt32(499236004) } - + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(backgroundColor!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {serializeInt32(secondBackgroundColor!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 5) != 0 {serializeInt32(thirdBackgroundColor!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 6) != 0 {serializeInt32(fourthBackgroundColor!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {serializeInt32(intensity!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {serializeInt32(rotation!, buffer: buffer, boxed: false)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .themes(let hash, let themes): - return ("themes", [("hash", hash as Any), ("themes", themes as Any)]) - case .themesNotModified: - return ("themesNotModified", []) + case .wallPaperSettings(let flags, let backgroundColor, let secondBackgroundColor, let thirdBackgroundColor, let fourthBackgroundColor, let intensity, let rotation): + return ("wallPaperSettings", [("flags", flags as Any), ("backgroundColor", backgroundColor as Any), ("secondBackgroundColor", secondBackgroundColor as Any), ("thirdBackgroundColor", thirdBackgroundColor as Any), ("fourthBackgroundColor", fourthBackgroundColor as Any), ("intensity", intensity as Any), ("rotation", rotation as Any)]) } } - public static func parse_themes(_ reader: BufferReader) -> Themes? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.Theme]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Theme.self) - } + public static func parse_wallPaperSettings(_ reader: BufferReader) -> WallPaperSettings? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } + var _3: Int32? + if Int(_1!) & Int(1 << 4) != 0 {_3 = reader.readInt32() } + var _4: Int32? + if Int(_1!) & Int(1 << 5) != 0 {_4 = reader.readInt32() } + var _5: Int32? + if Int(_1!) & Int(1 << 6) != 0 {_5 = reader.readInt32() } + var _6: Int32? + if Int(_1!) & Int(1 << 3) != 0 {_6 = reader.readInt32() } + var _7: Int32? + if Int(_1!) & Int(1 << 4) != 0 {_7 = reader.readInt32() } let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.Themes.themes(hash: _1!, themes: _2!) + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 4) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 5) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 6) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.WallPaperSettings.wallPaperSettings(flags: _1!, backgroundColor: _2, secondBackgroundColor: _3, thirdBackgroundColor: _4, fourthBackgroundColor: _5, intensity: _6, rotation: _7) } else { return nil } } - public static func parse_themesNotModified(_ reader: BufferReader) -> Themes? { - return Api.account.Themes.themesNotModified - } } } -public extension Api.account { - enum TmpPassword: TypeConstructorDescription { - case tmpPassword(tmpPassword: Buffer, validUntil: Int32) +public extension Api { + enum WebAuthorization: TypeConstructorDescription { + case webAuthorization(hash: Int64, botId: Int64, domain: String, browser: String, platform: String, dateCreated: Int32, dateActive: Int32, ip: String, region: String) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .tmpPassword(let tmpPassword, let validUntil): + case .webAuthorization(let hash, let botId, let domain, let browser, let platform, let dateCreated, let dateActive, let ip, let region): if boxed { - buffer.appendInt32(-614138572) + buffer.appendInt32(-1493633966) } - serializeBytes(tmpPassword, buffer: buffer, boxed: false) - serializeInt32(validUntil, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + serializeInt64(botId, buffer: buffer, boxed: false) + serializeString(domain, buffer: buffer, boxed: false) + serializeString(browser, buffer: buffer, boxed: false) + serializeString(platform, buffer: buffer, boxed: false) + serializeInt32(dateCreated, buffer: buffer, boxed: false) + serializeInt32(dateActive, buffer: buffer, boxed: false) + serializeString(ip, buffer: buffer, boxed: false) + serializeString(region, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .tmpPassword(let tmpPassword, let validUntil): - return ("tmpPassword", [("tmpPassword", tmpPassword as Any), ("validUntil", validUntil as Any)]) + case .webAuthorization(let hash, let botId, let domain, let browser, let platform, let dateCreated, let dateActive, let ip, let region): + return ("webAuthorization", [("hash", hash as Any), ("botId", botId as Any), ("domain", domain as Any), ("browser", browser as Any), ("platform", platform as Any), ("dateCreated", dateCreated as Any), ("dateActive", dateActive as Any), ("ip", ip as Any), ("region", region as Any)]) } } - public static func parse_tmpPassword(_ reader: BufferReader) -> TmpPassword? { - var _1: Buffer? - _1 = parseBytes(reader) - var _2: Int32? - _2 = reader.readInt32() + public static func parse_webAuthorization(_ reader: BufferReader) -> WebAuthorization? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int64? + _2 = reader.readInt64() + var _3: String? + _3 = parseString(reader) + var _4: String? + _4 = parseString(reader) + var _5: String? + _5 = parseString(reader) + var _6: Int32? + _6 = reader.readInt32() + var _7: Int32? + _7 = reader.readInt32() + var _8: String? + _8 = parseString(reader) + var _9: String? + _9 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.TmpPassword.tmpPassword(tmpPassword: _1!, validUntil: _2!) + 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 + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return Api.WebAuthorization.webAuthorization(hash: _1!, botId: _2!, domain: _3!, browser: _4!, platform: _5!, dateCreated: _6!, dateActive: _7!, ip: _8!, region: _9!) } else { return nil @@ -1180,108 +1282,298 @@ public extension Api.account { } } -public extension Api.account { - enum WallPapers: TypeConstructorDescription { - case wallPapers(hash: Int64, wallpapers: [Api.WallPaper]) - case wallPapersNotModified +public extension Api { + enum WebDocument: TypeConstructorDescription { + case webDocument(url: String, accessHash: Int64, size: Int32, mimeType: String, attributes: [Api.DocumentAttribute]) + case webDocumentNoProxy(url: String, size: Int32, mimeType: String, attributes: [Api.DocumentAttribute]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .wallPapers(let hash, let wallpapers): + case .webDocument(let url, let accessHash, let size, let mimeType, let attributes): if boxed { - buffer.appendInt32(-842824308) + buffer.appendInt32(475467473) } - serializeInt64(hash, buffer: buffer, boxed: false) + serializeString(url, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + serializeInt32(size, buffer: buffer, boxed: false) + serializeString(mimeType, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(wallpapers.count)) - for item in wallpapers { + buffer.appendInt32(Int32(attributes.count)) + for item in attributes { item.serialize(buffer, true) } break - case .wallPapersNotModified: + case .webDocumentNoProxy(let url, let size, let mimeType, let attributes): if boxed { - buffer.appendInt32(471437699) + buffer.appendInt32(-104284986) + } + serializeString(url, buffer: buffer, boxed: false) + serializeInt32(size, buffer: buffer, boxed: false) + serializeString(mimeType, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(attributes.count)) + for item in attributes { + item.serialize(buffer, true) } - break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .wallPapers(let hash, let wallpapers): - return ("wallPapers", [("hash", hash as Any), ("wallpapers", wallpapers as Any)]) - case .wallPapersNotModified: - return ("wallPapersNotModified", []) + case .webDocument(let url, let accessHash, let size, let mimeType, let attributes): + return ("webDocument", [("url", url as Any), ("accessHash", accessHash as Any), ("size", size as Any), ("mimeType", mimeType as Any), ("attributes", attributes as Any)]) + case .webDocumentNoProxy(let url, let size, let mimeType, let attributes): + return ("webDocumentNoProxy", [("url", url as Any), ("size", size as Any), ("mimeType", mimeType as Any), ("attributes", attributes as Any)]) } } - public static func parse_wallPapers(_ reader: BufferReader) -> WallPapers? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.WallPaper]? + public static func parse_webDocument(_ reader: BufferReader) -> WebDocument? { + var _1: String? + _1 = parseString(reader) + var _2: Int64? + _2 = reader.readInt64() + var _3: Int32? + _3 = reader.readInt32() + var _4: String? + _4 = parseString(reader) + var _5: [Api.DocumentAttribute]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.WallPaper.self) + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DocumentAttribute.self) } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.WallPapers.wallPapers(hash: _1!, wallpapers: _2!) + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.WebDocument.webDocument(url: _1!, accessHash: _2!, size: _3!, mimeType: _4!, attributes: _5!) } else { return nil } } - public static func parse_wallPapersNotModified(_ reader: BufferReader) -> WallPapers? { - return Api.account.WallPapers.wallPapersNotModified + public static func parse_webDocumentNoProxy(_ reader: BufferReader) -> WebDocument? { + var _1: String? + _1 = parseString(reader) + var _2: Int32? + _2 = reader.readInt32() + var _3: String? + _3 = parseString(reader) + var _4: [Api.DocumentAttribute]? + if let _ = reader.readInt32() { + _4 = 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 + if _c1 && _c2 && _c3 && _c4 { + return Api.WebDocument.webDocumentNoProxy(url: _1!, size: _2!, mimeType: _3!, attributes: _4!) + } + else { + return nil + } } } } -public extension Api.account { - enum WebAuthorizations: TypeConstructorDescription { - case webAuthorizations(authorizations: [Api.WebAuthorization], users: [Api.User]) +public extension Api { + enum WebPage: TypeConstructorDescription { + case webPage(flags: Int32, id: Int64, url: String, displayUrl: String, hash: Int32, type: String?, siteName: String?, title: String?, description: String?, photo: Api.Photo?, embedUrl: String?, embedType: String?, embedWidth: Int32?, embedHeight: Int32?, duration: Int32?, author: String?, document: Api.Document?, cachedPage: Api.Page?, attributes: [Api.WebPageAttribute]?) + case webPageEmpty(flags: Int32, id: Int64, url: String?) + case webPageNotModified(flags: Int32, cachedPageViews: Int32?) + case webPagePending(flags: Int32, id: Int64, url: String?, date: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .webAuthorizations(let authorizations, let users): + case .webPage(let flags, let id, let url, let displayUrl, let hash, let type, let siteName, let title, let description, let photo, let embedUrl, let embedType, let embedWidth, let embedHeight, let duration, let author, let document, let cachedPage, let attributes): if boxed { - buffer.appendInt32(-313079300) + buffer.appendInt32(-392411726) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(authorizations.count)) - for item in authorizations { + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + serializeString(url, buffer: buffer, boxed: false) + serializeString(displayUrl, buffer: buffer, boxed: false) + serializeInt32(hash, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(type!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(siteName!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {serializeString(description!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {photo!.serialize(buffer, true)} + if Int(flags) & Int(1 << 5) != 0 {serializeString(embedUrl!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 5) != 0 {serializeString(embedType!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 6) != 0 {serializeInt32(embedWidth!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 6) != 0 {serializeInt32(embedHeight!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 7) != 0 {serializeInt32(duration!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 8) != 0 {serializeString(author!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 9) != 0 {document!.serialize(buffer, true)} + if Int(flags) & Int(1 << 10) != 0 {cachedPage!.serialize(buffer, true)} + if Int(flags) & Int(1 << 12) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(attributes!.count)) + for item in attributes! { item.serialize(buffer, true) + }} + break + case .webPageEmpty(let flags, let id, let url): + if boxed { + buffer.appendInt32(555358088) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(url!, buffer: buffer, boxed: false)} + break + case .webPageNotModified(let flags, let cachedPageViews): + if boxed { + buffer.appendInt32(1930545681) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(cachedPageViews!, buffer: buffer, boxed: false)} + break + case .webPagePending(let flags, let id, let url, let date): + if boxed { + buffer.appendInt32(-1328464313) } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(url!, buffer: buffer, boxed: false)} + serializeInt32(date, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .webAuthorizations(let authorizations, let users): - return ("webAuthorizations", [("authorizations", authorizations as Any), ("users", users as Any)]) + case .webPage(let flags, let id, let url, let displayUrl, let hash, let type, let siteName, let title, let description, let photo, let embedUrl, let embedType, let embedWidth, let embedHeight, let duration, let author, let document, let cachedPage, let attributes): + return ("webPage", [("flags", flags as Any), ("id", id as Any), ("url", url as Any), ("displayUrl", displayUrl as Any), ("hash", hash as Any), ("type", type as Any), ("siteName", siteName as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("embedUrl", embedUrl as Any), ("embedType", embedType as Any), ("embedWidth", embedWidth as Any), ("embedHeight", embedHeight as Any), ("duration", duration as Any), ("author", author as Any), ("document", document as Any), ("cachedPage", cachedPage as Any), ("attributes", attributes as Any)]) + case .webPageEmpty(let flags, let id, let url): + return ("webPageEmpty", [("flags", flags as Any), ("id", id as Any), ("url", url as Any)]) + case .webPageNotModified(let flags, let cachedPageViews): + return ("webPageNotModified", [("flags", flags as Any), ("cachedPageViews", cachedPageViews as Any)]) + case .webPagePending(let flags, let id, let url, let date): + return ("webPagePending", [("flags", flags as Any), ("id", id as Any), ("url", url as Any), ("date", date as Any)]) } } - public static func parse_webAuthorizations(_ reader: BufferReader) -> WebAuthorizations? { - var _1: [Api.WebAuthorization]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.WebAuthorization.self) + public static func parse_webPage(_ reader: BufferReader) -> WebPage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: String? + _3 = parseString(reader) + var _4: String? + _4 = parseString(reader) + var _5: Int32? + _5 = reader.readInt32() + var _6: String? + if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) } + var _7: String? + if Int(_1!) & Int(1 << 1) != 0 {_7 = parseString(reader) } + var _8: String? + if Int(_1!) & Int(1 << 2) != 0 {_8 = parseString(reader) } + var _9: String? + if Int(_1!) & Int(1 << 3) != 0 {_9 = parseString(reader) } + var _10: Api.Photo? + if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() { + _10 = Api.parse(reader, signature: signature) as? Api.Photo + } } + var _11: String? + if Int(_1!) & Int(1 << 5) != 0 {_11 = parseString(reader) } + var _12: String? + if Int(_1!) & Int(1 << 5) != 0 {_12 = parseString(reader) } + var _13: Int32? + if Int(_1!) & Int(1 << 6) != 0 {_13 = reader.readInt32() } + var _14: Int32? + if Int(_1!) & Int(1 << 6) != 0 {_14 = reader.readInt32() } + var _15: Int32? + if Int(_1!) & Int(1 << 7) != 0 {_15 = reader.readInt32() } + var _16: String? + if Int(_1!) & Int(1 << 8) != 0 {_16 = parseString(reader) } + var _17: Api.Document? + if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() { + _17 = Api.parse(reader, signature: signature) as? Api.Document + } } + var _18: Api.Page? + if Int(_1!) & Int(1 << 10) != 0 {if let signature = reader.readInt32() { + _18 = Api.parse(reader, signature: signature) as? Api.Page + } } + var _19: [Api.WebPageAttribute]? + if Int(_1!) & Int(1 << 12) != 0 {if let _ = reader.readInt32() { + _19 = Api.parseVector(reader, elementSignature: 0, elementType: Api.WebPageAttribute.self) + } } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil + let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil + let _c9 = (Int(_1!) & Int(1 << 3) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 4) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 5) == 0) || _11 != nil + let _c12 = (Int(_1!) & Int(1 << 5) == 0) || _12 != nil + let _c13 = (Int(_1!) & Int(1 << 6) == 0) || _13 != nil + let _c14 = (Int(_1!) & Int(1 << 6) == 0) || _14 != nil + let _c15 = (Int(_1!) & Int(1 << 7) == 0) || _15 != nil + let _c16 = (Int(_1!) & Int(1 << 8) == 0) || _16 != nil + let _c17 = (Int(_1!) & Int(1 << 9) == 0) || _17 != nil + let _c18 = (Int(_1!) & Int(1 << 10) == 0) || _18 != nil + let _c19 = (Int(_1!) & Int(1 << 12) == 0) || _19 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 { + return Api.WebPage.webPage(flags: _1!, id: _2!, url: _3!, displayUrl: _4!, hash: _5!, type: _6, siteName: _7, title: _8, description: _9, photo: _10, embedUrl: _11, embedType: _12, embedWidth: _13, embedHeight: _14, duration: _15, author: _16, document: _17, cachedPage: _18, attributes: _19) } - var _2: [Api.User]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + else { + return nil } + } + public static func parse_webPageEmpty(_ reader: BufferReader) -> WebPage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: String? + if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } let _c1 = _1 != nil let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.WebPage.webPageEmpty(flags: _1!, id: _2!, url: _3) + } + else { + return nil + } + } + public static func parse_webPageNotModified(_ reader: BufferReader) -> WebPage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil if _c1 && _c2 { - return Api.account.WebAuthorizations.webAuthorizations(authorizations: _1!, users: _2!) + return Api.WebPage.webPageNotModified(flags: _1!, cachedPageViews: _2) + } + else { + return nil + } + } + public static func parse_webPagePending(_ reader: BufferReader) -> WebPage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: String? + if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } + var _4: Int32? + _4 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.WebPage.webPagePending(flags: _1!, id: _2!, url: _3, date: _4!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api24.swift b/submodules/TelegramApi/Sources/Api24.swift index 95534e002c8..55a953bc119 100644 --- a/submodules/TelegramApi/Sources/Api24.swift +++ b/submodules/TelegramApi/Sources/Api24.swift @@ -1,75 +1,83 @@ -public extension Api.auth { - enum Authorization: TypeConstructorDescription { - case authorization(flags: Int32, otherwiseReloginDays: Int32?, tmpSessions: Int32?, futureAuthToken: Buffer?, user: Api.User) - case authorizationSignUpRequired(flags: Int32, termsOfService: Api.help.TermsOfService?) +public extension Api { + indirect enum WebPageAttribute: TypeConstructorDescription { + case webPageAttributeStory(flags: Int32, peer: Api.Peer, id: Int32, story: Api.StoryItem?) + case webPageAttributeTheme(flags: Int32, documents: [Api.Document]?, settings: Api.ThemeSettings?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .authorization(let flags, let otherwiseReloginDays, let tmpSessions, let futureAuthToken, let user): + case .webPageAttributeStory(let flags, let peer, let id, let story): if boxed { - buffer.appendInt32(782418132) + buffer.appendInt32(781501415) } serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(otherwiseReloginDays!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(tmpSessions!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeBytes(futureAuthToken!, buffer: buffer, boxed: false)} - user.serialize(buffer, true) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {story!.serialize(buffer, true)} break - case .authorizationSignUpRequired(let flags, let termsOfService): + case .webPageAttributeTheme(let flags, let documents, let settings): if boxed { - buffer.appendInt32(1148485274) + buffer.appendInt32(1421174295) } serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {termsOfService!.serialize(buffer, true)} + if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(documents!.count)) + for item in documents! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 1) != 0 {settings!.serialize(buffer, true)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .authorization(let flags, let otherwiseReloginDays, let tmpSessions, let futureAuthToken, let user): - return ("authorization", [("flags", flags as Any), ("otherwiseReloginDays", otherwiseReloginDays as Any), ("tmpSessions", tmpSessions as Any), ("futureAuthToken", futureAuthToken as Any), ("user", user as Any)]) - case .authorizationSignUpRequired(let flags, let termsOfService): - return ("authorizationSignUpRequired", [("flags", flags as Any), ("termsOfService", termsOfService as Any)]) + case .webPageAttributeStory(let flags, let peer, let id, let story): + return ("webPageAttributeStory", [("flags", flags as Any), ("peer", peer as Any), ("id", id as Any), ("story", story as Any)]) + case .webPageAttributeTheme(let flags, let documents, let settings): + return ("webPageAttributeTheme", [("flags", flags as Any), ("documents", documents as Any), ("settings", settings as Any)]) } } - public static func parse_authorization(_ reader: BufferReader) -> Authorization? { + public static func parse_webPageAttributeStory(_ reader: BufferReader) -> WebPageAttribute? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() } - var _3: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } - var _4: Buffer? - if Int(_1!) & Int(1 << 2) != 0 {_4 = parseBytes(reader) } - var _5: Api.User? + var _2: Api.Peer? if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.User + _2 = Api.parse(reader, signature: signature) as? Api.Peer } + var _3: Int32? + _3 = reader.readInt32() + var _4: Api.StoryItem? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.StoryItem + } } let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.auth.Authorization.authorization(flags: _1!, otherwiseReloginDays: _2, tmpSessions: _3, futureAuthToken: _4, user: _5!) + 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.WebPageAttribute.webPageAttributeStory(flags: _1!, peer: _2!, id: _3!, story: _4) } else { return nil } } - public static func parse_authorizationSignUpRequired(_ reader: BufferReader) -> Authorization? { + public static func parse_webPageAttributeTheme(_ reader: BufferReader) -> WebPageAttribute? { var _1: Int32? _1 = reader.readInt32() - var _2: Api.help.TermsOfService? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.help.TermsOfService + var _2: [Api.Document]? + if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + } } + var _3: Api.ThemeSettings? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.ThemeSettings } } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.auth.Authorization.authorizationSignUpRequired(flags: _1!, termsOfService: _2) + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.WebPageAttribute.webPageAttributeTheme(flags: _1!, documents: _2, settings: _3) } else { return nil @@ -78,114 +86,80 @@ public extension Api.auth { } } -public extension Api.auth { - enum CodeType: TypeConstructorDescription { - case codeTypeCall - case codeTypeFlashCall - case codeTypeFragmentSms - case codeTypeMissedCall - case codeTypeSms +public extension Api { + enum WebViewMessageSent: TypeConstructorDescription { + case webViewMessageSent(flags: Int32, msgId: Api.InputBotInlineMessageID?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .codeTypeCall: - if boxed { - buffer.appendInt32(1948046307) - } - - break - case .codeTypeFlashCall: - if boxed { - buffer.appendInt32(577556219) - } - - break - case .codeTypeFragmentSms: - if boxed { - buffer.appendInt32(116234636) - } - - break - case .codeTypeMissedCall: - if boxed { - buffer.appendInt32(-702884114) - } - - break - case .codeTypeSms: + case .webViewMessageSent(let flags, let msgId): if boxed { - buffer.appendInt32(1923290508) + buffer.appendInt32(211046684) } - + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {msgId!.serialize(buffer, true)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .codeTypeCall: - return ("codeTypeCall", []) - case .codeTypeFlashCall: - return ("codeTypeFlashCall", []) - case .codeTypeFragmentSms: - return ("codeTypeFragmentSms", []) - case .codeTypeMissedCall: - return ("codeTypeMissedCall", []) - case .codeTypeSms: - return ("codeTypeSms", []) + case .webViewMessageSent(let flags, let msgId): + return ("webViewMessageSent", [("flags", flags as Any), ("msgId", msgId as Any)]) } } - public static func parse_codeTypeCall(_ reader: BufferReader) -> CodeType? { - return Api.auth.CodeType.codeTypeCall - } - public static func parse_codeTypeFlashCall(_ reader: BufferReader) -> CodeType? { - return Api.auth.CodeType.codeTypeFlashCall - } - public static func parse_codeTypeFragmentSms(_ reader: BufferReader) -> CodeType? { - return Api.auth.CodeType.codeTypeFragmentSms - } - public static func parse_codeTypeMissedCall(_ reader: BufferReader) -> CodeType? { - return Api.auth.CodeType.codeTypeMissedCall - } - public static func parse_codeTypeSms(_ reader: BufferReader) -> CodeType? { - return Api.auth.CodeType.codeTypeSms + public static func parse_webViewMessageSent(_ reader: BufferReader) -> WebViewMessageSent? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.InputBotInlineMessageID? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.InputBotInlineMessageID + } } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + if _c1 && _c2 { + return Api.WebViewMessageSent.webViewMessageSent(flags: _1!, msgId: _2) + } + else { + return nil + } } } } -public extension Api.auth { - enum ExportedAuthorization: TypeConstructorDescription { - case exportedAuthorization(id: Int64, bytes: Buffer) +public extension Api { + enum WebViewResult: TypeConstructorDescription { + case webViewResultUrl(queryId: Int64, url: String) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .exportedAuthorization(let id, let bytes): + case .webViewResultUrl(let queryId, let url): if boxed { - buffer.appendInt32(-1271602504) + buffer.appendInt32(202659196) } - serializeInt64(id, buffer: buffer, boxed: false) - serializeBytes(bytes, buffer: buffer, boxed: false) + serializeInt64(queryId, buffer: buffer, boxed: false) + serializeString(url, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .exportedAuthorization(let id, let bytes): - return ("exportedAuthorization", [("id", id as Any), ("bytes", bytes as Any)]) + case .webViewResultUrl(let queryId, let url): + return ("webViewResultUrl", [("queryId", queryId as Any), ("url", url as Any)]) } } - public static func parse_exportedAuthorization(_ reader: BufferReader) -> ExportedAuthorization? { + public static func parse_webViewResultUrl(_ reader: BufferReader) -> WebViewResult? { var _1: Int64? _1 = reader.readInt64() - var _2: Buffer? - _2 = parseBytes(reader) + var _2: String? + _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil if _c1 && _c2 { - return Api.auth.ExportedAuthorization.exportedAuthorization(id: _1!, bytes: _2!) + return Api.WebViewResult.webViewResultUrl(queryId: _1!, url: _2!) } else { return nil @@ -194,38 +168,78 @@ public extension Api.auth { } } -public extension Api.auth { - enum LoggedOut: TypeConstructorDescription { - case loggedOut(flags: Int32, futureAuthToken: Buffer?) +public extension Api.account { + enum AuthorizationForm: TypeConstructorDescription { + case authorizationForm(flags: Int32, requiredTypes: [Api.SecureRequiredType], values: [Api.SecureValue], errors: [Api.SecureValueError], users: [Api.User], privacyPolicyUrl: String?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .loggedOut(let flags, let futureAuthToken): + case .authorizationForm(let flags, let requiredTypes, let values, let errors, let users, let privacyPolicyUrl): if boxed { - buffer.appendInt32(-1012759713) + buffer.appendInt32(-1389486888) } serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeBytes(futureAuthToken!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(requiredTypes.count)) + for item in requiredTypes { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(values.count)) + for item in values { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(errors.count)) + for item in errors { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + if Int(flags) & Int(1 << 0) != 0 {serializeString(privacyPolicyUrl!, buffer: buffer, boxed: false)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .loggedOut(let flags, let futureAuthToken): - return ("loggedOut", [("flags", flags as Any), ("futureAuthToken", futureAuthToken as Any)]) + case .authorizationForm(let flags, let requiredTypes, let values, let errors, let users, let privacyPolicyUrl): + return ("authorizationForm", [("flags", flags as Any), ("requiredTypes", requiredTypes as Any), ("values", values as Any), ("errors", errors as Any), ("users", users as Any), ("privacyPolicyUrl", privacyPolicyUrl as Any)]) } } - public static func parse_loggedOut(_ reader: BufferReader) -> LoggedOut? { + public static func parse_authorizationForm(_ reader: BufferReader) -> AuthorizationForm? { var _1: Int32? _1 = reader.readInt32() - var _2: Buffer? - if Int(_1!) & Int(1 << 0) != 0 {_2 = parseBytes(reader) } + var _2: [Api.SecureRequiredType]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureRequiredType.self) + } + var _3: [Api.SecureValue]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureValue.self) + } + var _4: [Api.SecureValueError]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureValueError.self) + } + var _5: [Api.User]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + var _6: String? + if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) } let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.auth.LoggedOut.loggedOut(flags: _1!, futureAuthToken: _2) + 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.AuthorizationForm.authorizationForm(flags: _1!, requiredTypes: _2!, values: _3!, errors: _4!, users: _5!, privacyPolicyUrl: _6) } else { return nil @@ -234,84 +248,174 @@ public extension Api.auth { } } -public extension Api.auth { - enum LoginToken: TypeConstructorDescription { - case loginToken(expires: Int32, token: Buffer) - case loginTokenMigrateTo(dcId: Int32, token: Buffer) - case loginTokenSuccess(authorization: Api.auth.Authorization) +public extension Api.account { + enum Authorizations: TypeConstructorDescription { + case authorizations(authorizationTtlDays: Int32, authorizations: [Api.Authorization]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .loginToken(let expires, let token): - if boxed { - buffer.appendInt32(1654593920) - } - serializeInt32(expires, buffer: buffer, boxed: false) - serializeBytes(token, buffer: buffer, boxed: false) - break - case .loginTokenMigrateTo(let dcId, let token): + case .authorizations(let authorizationTtlDays, let authorizations): if boxed { - buffer.appendInt32(110008598) + buffer.appendInt32(1275039392) } - serializeInt32(dcId, buffer: buffer, boxed: false) - serializeBytes(token, buffer: buffer, boxed: false) - break - case .loginTokenSuccess(let authorization): - if boxed { - buffer.appendInt32(957176926) + serializeInt32(authorizationTtlDays, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(authorizations.count)) + for item in authorizations { + item.serialize(buffer, true) } - authorization.serialize(buffer, true) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .loginToken(let expires, let token): - return ("loginToken", [("expires", expires as Any), ("token", token as Any)]) - case .loginTokenMigrateTo(let dcId, let token): - return ("loginTokenMigrateTo", [("dcId", dcId as Any), ("token", token as Any)]) - case .loginTokenSuccess(let authorization): - return ("loginTokenSuccess", [("authorization", authorization as Any)]) + case .authorizations(let authorizationTtlDays, let authorizations): + return ("authorizations", [("authorizationTtlDays", authorizationTtlDays as Any), ("authorizations", authorizations as Any)]) } } - public static func parse_loginToken(_ reader: BufferReader) -> LoginToken? { + public static func parse_authorizations(_ reader: BufferReader) -> Authorizations? { var _1: Int32? _1 = reader.readInt32() - var _2: Buffer? - _2 = parseBytes(reader) + var _2: [Api.Authorization]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Authorization.self) + } let _c1 = _1 != nil let _c2 = _2 != nil if _c1 && _c2 { - return Api.auth.LoginToken.loginToken(expires: _1!, token: _2!) + return Api.account.Authorizations.authorizations(authorizationTtlDays: _1!, authorizations: _2!) } else { return nil } } - public static func parse_loginTokenMigrateTo(_ reader: BufferReader) -> LoginToken? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Buffer? - _2 = parseBytes(reader) + + } +} +public extension Api.account { + enum AutoDownloadSettings: TypeConstructorDescription { + case autoDownloadSettings(low: Api.AutoDownloadSettings, medium: Api.AutoDownloadSettings, high: Api.AutoDownloadSettings) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .autoDownloadSettings(let low, let medium, let high): + if boxed { + buffer.appendInt32(1674235686) + } + low.serialize(buffer, true) + medium.serialize(buffer, true) + high.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .autoDownloadSettings(let low, let medium, let high): + return ("autoDownloadSettings", [("low", low as Any), ("medium", medium as Any), ("high", high as Any)]) + } + } + + public static func parse_autoDownloadSettings(_ reader: BufferReader) -> AutoDownloadSettings? { + var _1: Api.AutoDownloadSettings? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.AutoDownloadSettings + } + var _2: Api.AutoDownloadSettings? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.AutoDownloadSettings + } + var _3: Api.AutoDownloadSettings? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.AutoDownloadSettings + } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.auth.LoginToken.loginTokenMigrateTo(dcId: _1!, token: _2!) + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.account.AutoDownloadSettings.autoDownloadSettings(low: _1!, medium: _2!, high: _3!) } else { return nil } } - public static func parse_loginTokenSuccess(_ reader: BufferReader) -> LoginToken? { - var _1: Api.auth.Authorization? + + } +} +public extension Api.account { + enum AutoSaveSettings: TypeConstructorDescription { + case autoSaveSettings(usersSettings: Api.AutoSaveSettings, chatsSettings: Api.AutoSaveSettings, broadcastsSettings: Api.AutoSaveSettings, exceptions: [Api.AutoSaveException], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .autoSaveSettings(let usersSettings, let chatsSettings, let broadcastsSettings, let exceptions, let chats, let users): + if boxed { + buffer.appendInt32(1279133341) + } + usersSettings.serialize(buffer, true) + chatsSettings.serialize(buffer, true) + broadcastsSettings.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(exceptions.count)) + for item in exceptions { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .autoSaveSettings(let usersSettings, let chatsSettings, let broadcastsSettings, let exceptions, let chats, let users): + return ("autoSaveSettings", [("usersSettings", usersSettings as Any), ("chatsSettings", chatsSettings as Any), ("broadcastsSettings", broadcastsSettings as Any), ("exceptions", exceptions as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_autoSaveSettings(_ reader: BufferReader) -> AutoSaveSettings? { + var _1: Api.AutoSaveSettings? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.AutoSaveSettings + } + var _2: Api.AutoSaveSettings? if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.auth.Authorization + _2 = Api.parse(reader, signature: signature) as? Api.AutoSaveSettings + } + var _3: Api.AutoSaveSettings? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.AutoSaveSettings + } + var _4: [Api.AutoSaveException]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.AutoSaveException.self) + } + var _5: [Api.Chat]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _6: [Api.User]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil - if _c1 { - return Api.auth.LoginToken.loginTokenSuccess(authorization: _1!) + 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.account.AutoSaveSettings.autoSaveSettings(usersSettings: _1!, chatsSettings: _2!, broadcastsSettings: _3!, exceptions: _4!, chats: _5!, users: _6!) } else { return nil @@ -320,34 +424,34 @@ public extension Api.auth { } } -public extension Api.auth { - enum PasswordRecovery: TypeConstructorDescription { - case passwordRecovery(emailPattern: String) +public extension Api.account { + enum ContentSettings: TypeConstructorDescription { + case contentSettings(flags: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .passwordRecovery(let emailPattern): + case .contentSettings(let flags): if boxed { - buffer.appendInt32(326715557) + buffer.appendInt32(1474462241) } - serializeString(emailPattern, buffer: buffer, boxed: false) + serializeInt32(flags, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .passwordRecovery(let emailPattern): - return ("passwordRecovery", [("emailPattern", emailPattern as Any)]) + case .contentSettings(let flags): + return ("contentSettings", [("flags", flags as Any)]) } } - public static func parse_passwordRecovery(_ reader: BufferReader) -> PasswordRecovery? { - var _1: String? - _1 = parseString(reader) + public static func parse_contentSettings(_ reader: BufferReader) -> ContentSettings? { + var _1: Int32? + _1 = reader.readInt32() let _c1 = _1 != nil if _c1 { - return Api.auth.PasswordRecovery.passwordRecovery(emailPattern: _1!) + return Api.account.ContentSettings.contentSettings(flags: _1!) } else { return nil @@ -356,76 +460,60 @@ 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 sentCodeSuccess(authorization: Api.auth.Authorization) +public extension Api.account { + enum EmailVerified: TypeConstructorDescription { + case emailVerified(email: String) + case emailVerifiedLogin(email: String, sentCode: Api.auth.SentCode) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .sentCode(let flags, let type, let phoneCodeHash, let nextType, let timeout): + case .emailVerified(let email): if boxed { - buffer.appendInt32(1577067778) + buffer.appendInt32(731303195) } - serializeInt32(flags, buffer: buffer, boxed: false) - type.serialize(buffer, true) - serializeString(phoneCodeHash, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {nextType!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)} + serializeString(email, buffer: buffer, boxed: false) break - case .sentCodeSuccess(let authorization): + case .emailVerifiedLogin(let email, let sentCode): if boxed { - buffer.appendInt32(596704836) + buffer.appendInt32(-507835039) } - authorization.serialize(buffer, true) + serializeString(email, buffer: buffer, boxed: false) + sentCode.serialize(buffer, true) break } } public func descriptionFields() -> (String, [(String, Any)]) { 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 .sentCodeSuccess(let authorization): - return ("sentCodeSuccess", [("authorization", authorization as Any)]) + case .emailVerified(let email): + return ("emailVerified", [("email", email as Any)]) + case .emailVerifiedLogin(let email, let sentCode): + return ("emailVerifiedLogin", [("email", email as Any), ("sentCode", sentCode as Any)]) } } - public static func parse_sentCode(_ reader: BufferReader) -> SentCode? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.auth.SentCodeType? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.auth.SentCodeType - } - var _3: String? - _3 = parseString(reader) - var _4: Api.auth.CodeType? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.auth.CodeType - } } - var _5: Int32? - if Int(_1!) & Int(1 << 2) != 0 {_5 = reader.readInt32() } + public static func parse_emailVerified(_ reader: BufferReader) -> EmailVerified? { + var _1: String? + _1 = parseString(reader) let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil - let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.auth.SentCode.sentCode(flags: _1!, type: _2!, phoneCodeHash: _3!, nextType: _4, timeout: _5) + if _c1 { + return Api.account.EmailVerified.emailVerified(email: _1!) } else { return nil } } - public static func parse_sentCodeSuccess(_ reader: BufferReader) -> SentCode? { - var _1: Api.auth.Authorization? + public static func parse_emailVerifiedLogin(_ reader: BufferReader) -> EmailVerified? { + var _1: String? + _1 = parseString(reader) + var _2: Api.auth.SentCode? if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.auth.Authorization + _2 = Api.parse(reader, signature: signature) as? Api.auth.SentCode } let _c1 = _1 != nil - if _c1 { - return Api.auth.SentCode.sentCodeSuccess(authorization: _1!) + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.account.EmailVerified.emailVerifiedLogin(email: _1!, sentCode: _2!) } else { return nil @@ -434,234 +522,198 @@ public extension Api.auth { } } -public extension Api.auth { - enum SentCodeType: TypeConstructorDescription { - case sentCodeTypeApp(length: Int32) - case sentCodeTypeCall(length: Int32) - case sentCodeTypeEmailCode(flags: Int32, emailPattern: String, length: Int32, resetAvailablePeriod: Int32?, resetPendingDate: Int32?) - case sentCodeTypeFirebaseSms(flags: Int32, nonce: Buffer?, receipt: String?, pushTimeout: Int32?, length: Int32) - case sentCodeTypeFlashCall(pattern: String) - case sentCodeTypeFragmentSms(url: String, length: Int32) - case sentCodeTypeMissedCall(prefix: String, length: Int32) - case sentCodeTypeSetUpEmailRequired(flags: Int32) - case sentCodeTypeSms(length: Int32) +public extension Api.account { + enum EmojiStatuses: TypeConstructorDescription { + case emojiStatuses(hash: Int64, statuses: [Api.EmojiStatus]) + case emojiStatusesNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .sentCodeTypeApp(let length): - if boxed { - buffer.appendInt32(1035688326) - } - serializeInt32(length, buffer: buffer, boxed: false) - break - case .sentCodeTypeCall(let length): - if boxed { - buffer.appendInt32(1398007207) - } - serializeInt32(length, buffer: buffer, boxed: false) - break - case .sentCodeTypeEmailCode(let flags, let emailPattern, let length, let resetAvailablePeriod, let resetPendingDate): - if boxed { - buffer.appendInt32(-196020837) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(emailPattern, buffer: buffer, boxed: false) - serializeInt32(length, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 3) != 0 {serializeInt32(resetAvailablePeriod!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {serializeInt32(resetPendingDate!, buffer: buffer, boxed: false)} - break - case .sentCodeTypeFirebaseSms(let flags, let nonce, let receipt, let pushTimeout, let length): - if boxed { - buffer.appendInt32(-444918734) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeBytes(nonce!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeString(receipt!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(pushTimeout!, buffer: buffer, boxed: false)} - serializeInt32(length, buffer: buffer, boxed: false) - break - case .sentCodeTypeFlashCall(let pattern): - if boxed { - buffer.appendInt32(-1425815847) - } - serializeString(pattern, buffer: buffer, boxed: false) - break - case .sentCodeTypeFragmentSms(let url, let length): - if boxed { - buffer.appendInt32(-648651719) - } - serializeString(url, buffer: buffer, boxed: false) - serializeInt32(length, buffer: buffer, boxed: false) - break - case .sentCodeTypeMissedCall(let prefix, let length): + case .emojiStatuses(let hash, let statuses): if boxed { - buffer.appendInt32(-2113903484) + buffer.appendInt32(-1866176559) } - serializeString(prefix, buffer: buffer, boxed: false) - serializeInt32(length, buffer: buffer, boxed: false) - break - case .sentCodeTypeSetUpEmailRequired(let flags): - if boxed { - buffer.appendInt32(-1521934870) + serializeInt64(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(statuses.count)) + for item in statuses { + item.serialize(buffer, true) } - serializeInt32(flags, buffer: buffer, boxed: false) break - case .sentCodeTypeSms(let length): + case .emojiStatusesNotModified: if boxed { - buffer.appendInt32(-1073693790) + buffer.appendInt32(-796072379) } - serializeInt32(length, buffer: buffer, boxed: false) + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .sentCodeTypeApp(let length): - return ("sentCodeTypeApp", [("length", length as Any)]) - case .sentCodeTypeCall(let length): - return ("sentCodeTypeCall", [("length", length as Any)]) - case .sentCodeTypeEmailCode(let flags, let emailPattern, let length, let resetAvailablePeriod, let resetPendingDate): - return ("sentCodeTypeEmailCode", [("flags", flags as Any), ("emailPattern", emailPattern as Any), ("length", length as Any), ("resetAvailablePeriod", resetAvailablePeriod as Any), ("resetPendingDate", resetPendingDate as Any)]) - case .sentCodeTypeFirebaseSms(let flags, let nonce, let receipt, let pushTimeout, let length): - return ("sentCodeTypeFirebaseSms", [("flags", flags as Any), ("nonce", nonce as Any), ("receipt", receipt as Any), ("pushTimeout", pushTimeout as Any), ("length", length as Any)]) - case .sentCodeTypeFlashCall(let pattern): - return ("sentCodeTypeFlashCall", [("pattern", pattern as Any)]) - case .sentCodeTypeFragmentSms(let url, let length): - return ("sentCodeTypeFragmentSms", [("url", url as Any), ("length", length as Any)]) - case .sentCodeTypeMissedCall(let prefix, let length): - return ("sentCodeTypeMissedCall", [("prefix", prefix as Any), ("length", length as Any)]) - case .sentCodeTypeSetUpEmailRequired(let flags): - return ("sentCodeTypeSetUpEmailRequired", [("flags", flags as Any)]) - case .sentCodeTypeSms(let length): - return ("sentCodeTypeSms", [("length", length as Any)]) - } - } - - public static func parse_sentCodeTypeApp(_ reader: BufferReader) -> SentCodeType? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.auth.SentCodeType.sentCodeTypeApp(length: _1!) - } - else { - return nil + case .emojiStatuses(let hash, let statuses): + return ("emojiStatuses", [("hash", hash as Any), ("statuses", statuses as Any)]) + case .emojiStatusesNotModified: + return ("emojiStatusesNotModified", []) + } + } + + public static func parse_emojiStatuses(_ reader: BufferReader) -> EmojiStatuses? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.EmojiStatus]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.EmojiStatus.self) } - } - public static func parse_sentCodeTypeCall(_ reader: BufferReader) -> SentCodeType? { - var _1: Int32? - _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.auth.SentCodeType.sentCodeTypeCall(length: _1!) + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.account.EmojiStatuses.emojiStatuses(hash: _1!, statuses: _2!) } else { return nil } } - public static func parse_sentCodeTypeEmailCode(_ reader: BufferReader) -> SentCodeType? { - var _1: Int32? - _1 = reader.readInt32() - var _2: String? - _2 = parseString(reader) - 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 << 4) != 0 {_5 = reader.readInt32() } - 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 << 4) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.auth.SentCodeType.sentCodeTypeEmailCode(flags: _1!, emailPattern: _2!, length: _3!, resetAvailablePeriod: _4, resetPendingDate: _5) - } - else { - return nil - } + public static func parse_emojiStatusesNotModified(_ reader: BufferReader) -> EmojiStatuses? { + return Api.account.EmojiStatuses.emojiStatusesNotModified } - public static func parse_sentCodeTypeFirebaseSms(_ reader: BufferReader) -> SentCodeType? { + + } +} +public extension Api.account { + enum Password: TypeConstructorDescription { + case password(flags: Int32, currentAlgo: Api.PasswordKdfAlgo?, srpB: Buffer?, srpId: Int64?, hint: String?, emailUnconfirmedPattern: String?, newAlgo: Api.PasswordKdfAlgo, newSecureAlgo: Api.SecurePasswordKdfAlgo, secureRandom: Buffer, pendingResetDate: Int32?, loginEmailPattern: String?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .password(let flags, let currentAlgo, let srpB, let srpId, let hint, let emailUnconfirmedPattern, let newAlgo, let newSecureAlgo, let secureRandom, let pendingResetDate, let loginEmailPattern): + if boxed { + buffer.appendInt32(-1787080453) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {currentAlgo!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {serializeBytes(srpB!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeInt64(srpId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {serializeString(hint!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {serializeString(emailUnconfirmedPattern!, buffer: buffer, boxed: false)} + newAlgo.serialize(buffer, true) + newSecureAlgo.serialize(buffer, true) + serializeBytes(secureRandom, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 5) != 0 {serializeInt32(pendingResetDate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 6) != 0 {serializeString(loginEmailPattern!, buffer: buffer, boxed: false)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .password(let flags, let currentAlgo, let srpB, let srpId, let hint, let emailUnconfirmedPattern, let newAlgo, let newSecureAlgo, let secureRandom, let pendingResetDate, let loginEmailPattern): + return ("password", [("flags", flags as Any), ("currentAlgo", currentAlgo as Any), ("srpB", srpB as Any), ("srpId", srpId as Any), ("hint", hint as Any), ("emailUnconfirmedPattern", emailUnconfirmedPattern as Any), ("newAlgo", newAlgo as Any), ("newSecureAlgo", newSecureAlgo as Any), ("secureRandom", secureRandom as Any), ("pendingResetDate", pendingResetDate as Any), ("loginEmailPattern", loginEmailPattern as Any)]) + } + } + + public static func parse_password(_ reader: BufferReader) -> Password? { var _1: Int32? _1 = reader.readInt32() - var _2: Buffer? - if Int(_1!) & Int(1 << 0) != 0 {_2 = parseBytes(reader) } - var _3: String? - if Int(_1!) & Int(1 << 1) != 0 {_3 = parseString(reader) } - var _4: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_4 = reader.readInt32() } - var _5: Int32? - _5 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.auth.SentCodeType.sentCodeTypeFirebaseSms(flags: _1!, nonce: _2, receipt: _3, pushTimeout: _4, length: _5!) - } - else { - return nil - } - } - public static func parse_sentCodeTypeFlashCall(_ reader: BufferReader) -> SentCodeType? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.auth.SentCodeType.sentCodeTypeFlashCall(pattern: _1!) - } - else { - return nil - } - } - public static func parse_sentCodeTypeFragmentSms(_ reader: BufferReader) -> SentCodeType? { - 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.auth.SentCodeType.sentCodeTypeFragmentSms(url: _1!, length: _2!) - } - else { - return nil - } - } - public static func parse_sentCodeTypeMissedCall(_ reader: BufferReader) -> SentCodeType? { - 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.auth.SentCodeType.sentCodeTypeMissedCall(prefix: _1!, length: _2!) - } - else { - return nil + var _2: Api.PasswordKdfAlgo? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.PasswordKdfAlgo + } } + var _3: Buffer? + if Int(_1!) & Int(1 << 2) != 0 {_3 = parseBytes(reader) } + var _4: Int64? + if Int(_1!) & Int(1 << 2) != 0 {_4 = reader.readInt64() } + var _5: String? + if Int(_1!) & Int(1 << 3) != 0 {_5 = parseString(reader) } + var _6: String? + if Int(_1!) & Int(1 << 4) != 0 {_6 = parseString(reader) } + var _7: Api.PasswordKdfAlgo? + if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.PasswordKdfAlgo } - } - public static func parse_sentCodeTypeSetUpEmailRequired(_ reader: BufferReader) -> SentCodeType? { - var _1: Int32? - _1 = reader.readInt32() + var _8: Api.SecurePasswordKdfAlgo? + if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.SecurePasswordKdfAlgo + } + var _9: Buffer? + _9 = parseBytes(reader) + var _10: Int32? + if Int(_1!) & Int(1 << 5) != 0 {_10 = reader.readInt32() } + var _11: String? + if Int(_1!) & Int(1 << 6) != 0 {_11 = parseString(reader) } let _c1 = _1 != nil - if _c1 { - return Api.auth.SentCodeType.sentCodeTypeSetUpEmailRequired(flags: _1!) + let _c2 = (Int(_1!) & Int(1 << 2) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 4) == 0) || _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + let _c9 = _9 != nil + let _c10 = (Int(_1!) & Int(1 << 5) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 6) == 0) || _11 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { + return Api.account.Password.password(flags: _1!, currentAlgo: _2, srpB: _3, srpId: _4, hint: _5, emailUnconfirmedPattern: _6, newAlgo: _7!, newSecureAlgo: _8!, secureRandom: _9!, pendingResetDate: _10, loginEmailPattern: _11) } else { return nil } } - public static func parse_sentCodeTypeSms(_ reader: BufferReader) -> SentCodeType? { + + } +} +public extension Api.account { + enum PasswordInputSettings: TypeConstructorDescription { + case passwordInputSettings(flags: Int32, newAlgo: Api.PasswordKdfAlgo?, newPasswordHash: Buffer?, hint: String?, email: String?, newSecureSettings: Api.SecureSecretSettings?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .passwordInputSettings(let flags, let newAlgo, let newPasswordHash, let hint, let email, let newSecureSettings): + if boxed { + buffer.appendInt32(-1036572727) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {newAlgo!.serialize(buffer, true)} + if Int(flags) & Int(1 << 0) != 0 {serializeBytes(newPasswordHash!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeString(hint!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(email!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {newSecureSettings!.serialize(buffer, true)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .passwordInputSettings(let flags, let newAlgo, let newPasswordHash, let hint, let email, let newSecureSettings): + return ("passwordInputSettings", [("flags", flags as Any), ("newAlgo", newAlgo as Any), ("newPasswordHash", newPasswordHash as Any), ("hint", hint as Any), ("email", email as Any), ("newSecureSettings", newSecureSettings as Any)]) + } + } + + public static func parse_passwordInputSettings(_ reader: BufferReader) -> PasswordInputSettings? { var _1: Int32? _1 = reader.readInt32() + var _2: Api.PasswordKdfAlgo? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.PasswordKdfAlgo + } } + var _3: Buffer? + if Int(_1!) & Int(1 << 0) != 0 {_3 = parseBytes(reader) } + 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) } + var _6: Api.SecureSecretSettings? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.SecureSecretSettings + } } let _c1 = _1 != nil - if _c1 { - return Api.auth.SentCodeType.sentCodeTypeSms(length: _1!) + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.account.PasswordInputSettings.passwordInputSettings(flags: _1!, newAlgo: _2, newPasswordHash: _3, hint: _4, email: _5, newSecureSettings: _6) } else { return nil @@ -670,42 +722,44 @@ public extension Api.auth { } } -public extension Api.bots { - enum BotInfo: TypeConstructorDescription { - case botInfo(name: String, about: String, description: String) +public extension Api.account { + enum PasswordSettings: TypeConstructorDescription { + case passwordSettings(flags: Int32, email: String?, secureSettings: Api.SecureSecretSettings?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .botInfo(let name, let about, let description): + case .passwordSettings(let flags, let email, let secureSettings): if boxed { - buffer.appendInt32(-391678544) + buffer.appendInt32(-1705233435) } - serializeString(name, buffer: buffer, boxed: false) - serializeString(about, buffer: buffer, boxed: false) - serializeString(description, buffer: buffer, boxed: false) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(email!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {secureSettings!.serialize(buffer, true)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .botInfo(let name, let about, let description): - return ("botInfo", [("name", name as Any), ("about", about as Any), ("description", description as Any)]) + case .passwordSettings(let flags, let email, let secureSettings): + return ("passwordSettings", [("flags", flags as Any), ("email", email as Any), ("secureSettings", secureSettings as Any)]) } } - public static func parse_botInfo(_ reader: BufferReader) -> BotInfo? { - var _1: String? - _1 = parseString(reader) + public static func parse_passwordSettings(_ reader: BufferReader) -> PasswordSettings? { + var _1: Int32? + _1 = reader.readInt32() var _2: String? - _2 = parseString(reader) - var _3: String? - _3 = parseString(reader) + if Int(_1!) & Int(1 << 0) != 0 {_2 = parseString(reader) } + var _3: Api.SecureSecretSettings? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.SecureSecretSettings + } } let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil if _c1 && _c2 && _c3 { - return Api.bots.BotInfo.botInfo(name: _1!, about: _2!, description: _3!) + return Api.account.PasswordSettings.passwordSettings(flags: _1!, email: _2, secureSettings: _3) } else { return nil @@ -714,19 +768,19 @@ public extension Api.bots { } } -public extension Api.channels { - enum AdminLogResults: TypeConstructorDescription { - case adminLogResults(events: [Api.ChannelAdminLogEvent], chats: [Api.Chat], users: [Api.User]) +public extension Api.account { + enum PrivacyRules: TypeConstructorDescription { + case privacyRules(rules: [Api.PrivacyRule], chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .adminLogResults(let events, let chats, let users): + case .privacyRules(let rules, let chats, let users): if boxed { - buffer.appendInt32(-309659827) + buffer.appendInt32(1352683077) } buffer.appendInt32(481674261) - buffer.appendInt32(Int32(events.count)) - for item in events { + buffer.appendInt32(Int32(rules.count)) + for item in rules { item.serialize(buffer, true) } buffer.appendInt32(481674261) @@ -745,15 +799,15 @@ public extension Api.channels { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .adminLogResults(let events, let chats, let users): - return ("adminLogResults", [("events", events as Any), ("chats", chats as Any), ("users", users as Any)]) + case .privacyRules(let rules, let chats, let users): + return ("privacyRules", [("rules", rules as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_adminLogResults(_ reader: BufferReader) -> AdminLogResults? { - var _1: [Api.ChannelAdminLogEvent]? + public static func parse_privacyRules(_ reader: BufferReader) -> PrivacyRules? { + var _1: [Api.PrivacyRule]? if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ChannelAdminLogEvent.self) + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PrivacyRule.self) } var _2: [Api.Chat]? if let _ = reader.readInt32() { @@ -767,7 +821,7 @@ public extension Api.channels { let _c2 = _2 != nil let _c3 = _3 != nil if _c1 && _c2 && _c3 { - return Api.channels.AdminLogResults.adminLogResults(events: _1!, chats: _2!, users: _3!) + return Api.account.PrivacyRules.privacyRules(rules: _1!, chats: _2!, users: _3!) } else { return nil @@ -776,56 +830,66 @@ public extension Api.channels { } } -public extension Api.channels { - enum ChannelParticipant: TypeConstructorDescription { - case channelParticipant(participant: Api.ChannelParticipant, chats: [Api.Chat], users: [Api.User]) +public extension Api.account { + enum ResetPasswordResult: TypeConstructorDescription { + case resetPasswordFailedWait(retryDate: Int32) + case resetPasswordOk + case resetPasswordRequestedWait(untilDate: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .channelParticipant(let participant, let chats, let users): + case .resetPasswordFailedWait(let retryDate): if boxed { - buffer.appendInt32(-541588713) + buffer.appendInt32(-478701471) } - participant.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) + serializeInt32(retryDate, buffer: buffer, boxed: false) + break + case .resetPasswordOk: + if boxed { + buffer.appendInt32(-383330754) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + + break + case .resetPasswordRequestedWait(let untilDate): + if boxed { + buffer.appendInt32(-370148227) } + serializeInt32(untilDate, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .channelParticipant(let participant, let chats, let users): - return ("channelParticipant", [("participant", participant as Any), ("chats", chats as Any), ("users", users as Any)]) + case .resetPasswordFailedWait(let retryDate): + return ("resetPasswordFailedWait", [("retryDate", retryDate as Any)]) + case .resetPasswordOk: + return ("resetPasswordOk", []) + case .resetPasswordRequestedWait(let untilDate): + return ("resetPasswordRequestedWait", [("untilDate", untilDate as Any)]) } } - public static func parse_channelParticipant(_ reader: BufferReader) -> ChannelParticipant? { - var _1: Api.ChannelParticipant? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.ChannelParticipant - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + public static func parse_resetPasswordFailedWait(_ reader: BufferReader) -> ResetPasswordResult? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.account.ResetPasswordResult.resetPasswordFailedWait(retryDate: _1!) } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + else { + return nil } + } + public static func parse_resetPasswordOk(_ reader: BufferReader) -> ResetPasswordResult? { + return Api.account.ResetPasswordResult.resetPasswordOk + } + public static func parse_resetPasswordRequestedWait(_ reader: BufferReader) -> ResetPasswordResult? { + var _1: Int32? + _1 = reader.readInt32() let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.channels.ChannelParticipant.channelParticipant(participant: _1!, chats: _2!, users: _3!) + if _c1 { + return Api.account.ResetPasswordResult.resetPasswordRequestedWait(untilDate: _1!) } else { return nil @@ -834,37 +898,77 @@ public extension Api.channels { } } -public extension Api.channels { - enum ChannelParticipants: TypeConstructorDescription { - case channelParticipants(count: Int32, participants: [Api.ChannelParticipant], chats: [Api.Chat], users: [Api.User]) - case channelParticipantsNotModified +public extension Api.account { + enum SavedRingtone: TypeConstructorDescription { + case savedRingtone + case savedRingtoneConverted(document: Api.Document) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .channelParticipants(let count, let participants, let chats, let users): + case .savedRingtone: if boxed { - buffer.appendInt32(-1699676497) + buffer.appendInt32(-1222230163) } - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(participants.count)) - for item in participants { - item.serialize(buffer, true) + + break + case .savedRingtoneConverted(let document): + if boxed { + buffer.appendInt32(523271863) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) + document.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .savedRingtone: + return ("savedRingtone", []) + case .savedRingtoneConverted(let document): + return ("savedRingtoneConverted", [("document", document as Any)]) + } + } + + public static func parse_savedRingtone(_ reader: BufferReader) -> SavedRingtone? { + return Api.account.SavedRingtone.savedRingtone + } + public static func parse_savedRingtoneConverted(_ reader: BufferReader) -> SavedRingtone? { + var _1: Api.Document? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Document + } + let _c1 = _1 != nil + if _c1 { + return Api.account.SavedRingtone.savedRingtoneConverted(document: _1!) + } + else { + return nil + } + } + + } +} +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(users.count)) - for item in users { + buffer.appendInt32(Int32(ringtones.count)) + for item in ringtones { item.serialize(buffer, true) } break - case .channelParticipantsNotModified: + case .savedRingtonesNotModified: if boxed { - buffer.appendInt32(-266911767) + buffer.appendInt32(-67704655) } break @@ -873,99 +977,201 @@ public extension Api.channels { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .channelParticipants(let count, let participants, let chats, let users): - return ("channelParticipants", [("count", count as Any), ("participants", participants as Any), ("chats", chats as Any), ("users", users as Any)]) - case .channelParticipantsNotModified: - return ("channelParticipantsNotModified", []) + case .savedRingtones(let hash, let ringtones): + return ("savedRingtones", [("hash", hash as Any), ("ringtones", ringtones as Any)]) + case .savedRingtonesNotModified: + return ("savedRingtonesNotModified", []) } } - public static func parse_channelParticipants(_ reader: BufferReader) -> ChannelParticipants? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.ChannelParticipant]? + 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.ChannelParticipant.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) } - var _3: [Api.Chat]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.account.SavedRingtones.savedRingtones(hash: _1!, ringtones: _2!) } - var _4: [Api.User]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + 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 - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.channels.ChannelParticipants.channelParticipants(count: _1!, participants: _2!, chats: _3!, users: _4!) + if _c1 && _c2 { + return Api.account.SentEmailCode.sentEmailCode(emailPattern: _1!, length: _2!) } else { return nil } } - public static func parse_channelParticipantsNotModified(_ reader: BufferReader) -> ChannelParticipants? { - return Api.channels.ChannelParticipants.channelParticipantsNotModified - } } } -public extension Api.channels { - enum SendAsPeers: TypeConstructorDescription { - case sendAsPeers(peers: [Api.SendAsPeer], chats: [Api.Chat], users: [Api.User]) +public extension Api.account { + enum Takeout: TypeConstructorDescription { + case takeout(id: Int64) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .sendAsPeers(let peers, let chats, let users): + case .takeout(let id): if boxed { - buffer.appendInt32(-191450938) + buffer.appendInt32(1304052993) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(peers.count)) - for item in peers { - item.serialize(buffer, true) + serializeInt64(id, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .takeout(let id): + return ("takeout", [("id", id as Any)]) + } + } + + public static func parse_takeout(_ reader: BufferReader) -> Takeout? { + var _1: Int64? + _1 = reader.readInt64() + let _c1 = _1 != nil + if _c1 { + return Api.account.Takeout.takeout(id: _1!) + } + else { + return nil + } + } + + } +} +public extension Api.account { + enum Themes: TypeConstructorDescription { + case themes(hash: Int64, themes: [Api.Theme]) + case themesNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .themes(let hash, let themes): + if boxed { + buffer.appendInt32(-1707242387) } + serializeInt64(hash, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { + buffer.appendInt32(Int32(themes.count)) + for item in themes { item.serialize(buffer, true) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + break + case .themesNotModified: + if boxed { + buffer.appendInt32(-199313886) } + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .sendAsPeers(let peers, let chats, let users): - return ("sendAsPeers", [("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)]) + case .themes(let hash, let themes): + return ("themes", [("hash", hash as Any), ("themes", themes as Any)]) + case .themesNotModified: + return ("themesNotModified", []) } } - public static func parse_sendAsPeers(_ reader: BufferReader) -> SendAsPeers? { - var _1: [Api.SendAsPeer]? + public static func parse_themes(_ reader: BufferReader) -> Themes? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.Theme]? if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SendAsPeer.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Theme.self) } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.account.Themes.themes(hash: _1!, themes: _2!) } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + else { + return nil } + } + public static func parse_themesNotModified(_ reader: BufferReader) -> Themes? { + return Api.account.Themes.themesNotModified + } + + } +} +public extension Api.account { + enum TmpPassword: TypeConstructorDescription { + case tmpPassword(tmpPassword: Buffer, validUntil: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .tmpPassword(let tmpPassword, let validUntil): + if boxed { + buffer.appendInt32(-614138572) + } + serializeBytes(tmpPassword, buffer: buffer, boxed: false) + serializeInt32(validUntil, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .tmpPassword(let tmpPassword, let validUntil): + return ("tmpPassword", [("tmpPassword", tmpPassword as Any), ("validUntil", validUntil as Any)]) + } + } + + public static func parse_tmpPassword(_ reader: BufferReader) -> TmpPassword? { + var _1: Buffer? + _1 = parseBytes(reader) + var _2: Int32? + _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.channels.SendAsPeers.sendAsPeers(peers: _1!, chats: _2!, users: _3!) + if _c1 && _c2 { + return Api.account.TmpPassword.tmpPassword(tmpPassword: _1!, validUntil: _2!) } else { return nil @@ -974,132 +1180,108 @@ public extension Api.channels { } } -public extension Api.chatlists { - enum ChatlistInvite: TypeConstructorDescription { - case chatlistInvite(flags: Int32, title: String, emoticon: String?, peers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) - case chatlistInviteAlready(filterId: Int32, missingPeers: [Api.Peer], alreadyPeers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) +public extension Api.account { + enum WallPapers: TypeConstructorDescription { + case wallPapers(hash: Int64, wallpapers: [Api.WallPaper]) + case wallPapersNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .chatlistInvite(let flags, let title, let emoticon, let peers, let chats, let users): + case .wallPapers(let hash, let wallpapers): if boxed { - buffer.appendInt32(500007837) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(emoticon!, buffer: buffer, boxed: false)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(peers.count)) - for item in peers { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) + buffer.appendInt32(-842824308) } + serializeInt64(hash, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { + buffer.appendInt32(Int32(wallpapers.count)) + for item in wallpapers { item.serialize(buffer, true) } break - case .chatlistInviteAlready(let filterId, let missingPeers, let alreadyPeers, let chats, let users): + case .wallPapersNotModified: if boxed { - buffer.appendInt32(-91752871) - } - serializeInt32(filterId, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(missingPeers.count)) - for item in missingPeers { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(alreadyPeers.count)) - for item in alreadyPeers { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + buffer.appendInt32(471437699) } + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .chatlistInvite(let flags, let title, let emoticon, let peers, let chats, let users): - return ("chatlistInvite", [("flags", flags as Any), ("title", title as Any), ("emoticon", emoticon as Any), ("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)]) - case .chatlistInviteAlready(let filterId, let missingPeers, let alreadyPeers, let chats, let users): - return ("chatlistInviteAlready", [("filterId", filterId as Any), ("missingPeers", missingPeers as Any), ("alreadyPeers", alreadyPeers as Any), ("chats", chats as Any), ("users", users as Any)]) + case .wallPapers(let hash, let wallpapers): + return ("wallPapers", [("hash", hash as Any), ("wallpapers", wallpapers as Any)]) + case .wallPapersNotModified: + return ("wallPapersNotModified", []) } } - public static func parse_chatlistInvite(_ reader: BufferReader) -> ChatlistInvite? { - var _1: Int32? - _1 = reader.readInt32() - var _2: String? - _2 = parseString(reader) - var _3: String? - if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } - var _4: [Api.Peer]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) - } - var _5: [Api.Chat]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _6: [Api.User]? + public static func parse_wallPapers(_ reader: BufferReader) -> WallPapers? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.WallPaper]? if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.WallPaper.self) } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.chatlists.ChatlistInvite.chatlistInvite(flags: _1!, title: _2!, emoticon: _3, peers: _4!, chats: _5!, users: _6!) + if _c1 && _c2 { + return Api.account.WallPapers.wallPapers(hash: _1!, wallpapers: _2!) } else { return nil } } - public static func parse_chatlistInviteAlready(_ reader: BufferReader) -> ChatlistInvite? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.Peer]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) - } - var _3: [Api.Peer]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) - } - var _4: [Api.Chat]? + public static func parse_wallPapersNotModified(_ reader: BufferReader) -> WallPapers? { + return Api.account.WallPapers.wallPapersNotModified + } + + } +} +public extension Api.account { + enum WebAuthorizations: TypeConstructorDescription { + case webAuthorizations(authorizations: [Api.WebAuthorization], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .webAuthorizations(let authorizations, let users): + if boxed { + buffer.appendInt32(-313079300) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(authorizations.count)) + for item in authorizations { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .webAuthorizations(let authorizations, let users): + return ("webAuthorizations", [("authorizations", authorizations as Any), ("users", users as Any)]) + } + } + + public static func parse_webAuthorizations(_ reader: BufferReader) -> WebAuthorizations? { + var _1: [Api.WebAuthorization]? if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.WebAuthorization.self) } - var _5: [Api.User]? + var _2: [Api.User]? if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.chatlists.ChatlistInvite.chatlistInviteAlready(filterId: _1!, missingPeers: _2!, alreadyPeers: _3!, chats: _4!, users: _5!) + if _c1 && _c2 { + return Api.account.WebAuthorizations.webAuthorizations(authorizations: _1!, users: _2!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api25.swift b/submodules/TelegramApi/Sources/Api25.swift index 6d2b3dab76d..95534e002c8 100644 --- a/submodules/TelegramApi/Sources/Api25.swift +++ b/submodules/TelegramApi/Sources/Api25.swift @@ -1,545 +1,75 @@ -public extension Api.chatlists { - enum ChatlistUpdates: TypeConstructorDescription { - case chatlistUpdates(missingPeers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .chatlistUpdates(let missingPeers, let chats, let users): - if boxed { - buffer.appendInt32(-1816295539) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(missingPeers.count)) - for item in missingPeers { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .chatlistUpdates(let missingPeers, let chats, let users): - return ("chatlistUpdates", [("missingPeers", missingPeers as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_chatlistUpdates(_ reader: BufferReader) -> ChatlistUpdates? { - var _1: [Api.Peer]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.chatlists.ChatlistUpdates.chatlistUpdates(missingPeers: _1!, chats: _2!, users: _3!) - } - else { - return nil - } - } - - } -} -public extension Api.chatlists { - enum ExportedChatlistInvite: TypeConstructorDescription { - case exportedChatlistInvite(filter: Api.DialogFilter, invite: Api.ExportedChatlistInvite) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .exportedChatlistInvite(let filter, let invite): - if boxed { - buffer.appendInt32(283567014) - } - filter.serialize(buffer, true) - invite.serialize(buffer, true) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .exportedChatlistInvite(let filter, let invite): - return ("exportedChatlistInvite", [("filter", filter as Any), ("invite", invite as Any)]) - } - } - - public static func parse_exportedChatlistInvite(_ reader: BufferReader) -> ExportedChatlistInvite? { - var _1: Api.DialogFilter? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.DialogFilter - } - var _2: Api.ExportedChatlistInvite? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.ExportedChatlistInvite - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.chatlists.ExportedChatlistInvite.exportedChatlistInvite(filter: _1!, invite: _2!) - } - else { - return nil - } - } - - } -} -public extension Api.chatlists { - enum ExportedInvites: TypeConstructorDescription { - case exportedInvites(invites: [Api.ExportedChatlistInvite], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .exportedInvites(let invites, let chats, let users): - if boxed { - buffer.appendInt32(279670215) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(invites.count)) - for item in invites { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .exportedInvites(let invites, let chats, let users): - return ("exportedInvites", [("invites", invites as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_exportedInvites(_ reader: BufferReader) -> ExportedInvites? { - var _1: [Api.ExportedChatlistInvite]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ExportedChatlistInvite.self) - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.chatlists.ExportedInvites.exportedInvites(invites: _1!, chats: _2!, users: _3!) - } - else { - return nil - } - } - - } -} -public extension Api.contacts { - enum Blocked: TypeConstructorDescription { - case blocked(blocked: [Api.PeerBlocked], chats: [Api.Chat], users: [Api.User]) - case blockedSlice(count: Int32, blocked: [Api.PeerBlocked], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .blocked(let blocked, let chats, let users): - if boxed { - buffer.appendInt32(182326673) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(blocked.count)) - for item in blocked { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - case .blockedSlice(let count, let blocked, let chats, let users): - if boxed { - buffer.appendInt32(-513392236) - } - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(blocked.count)) - for item in blocked { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .blocked(let blocked, let chats, let users): - return ("blocked", [("blocked", blocked as Any), ("chats", chats as Any), ("users", users as Any)]) - case .blockedSlice(let count, let blocked, let chats, let users): - return ("blockedSlice", [("count", count as Any), ("blocked", blocked as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_blocked(_ reader: BufferReader) -> Blocked? { - var _1: [Api.PeerBlocked]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PeerBlocked.self) - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.contacts.Blocked.blocked(blocked: _1!, chats: _2!, users: _3!) - } - else { - return nil - } - } - public static func parse_blockedSlice(_ reader: BufferReader) -> Blocked? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.PeerBlocked]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PeerBlocked.self) - } - var _3: [Api.Chat]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _4: [Api.User]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.contacts.Blocked.blockedSlice(count: _1!, blocked: _2!, chats: _3!, users: _4!) - } - else { - return nil - } - } - - } -} -public extension Api.contacts { - enum Contacts: TypeConstructorDescription { - case contacts(contacts: [Api.Contact], savedCount: Int32, users: [Api.User]) - case contactsNotModified - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .contacts(let contacts, let savedCount, let users): - if boxed { - buffer.appendInt32(-353862078) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(contacts.count)) - for item in contacts { - item.serialize(buffer, true) - } - serializeInt32(savedCount, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - case .contactsNotModified: - if boxed { - buffer.appendInt32(-1219778094) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .contacts(let contacts, let savedCount, let users): - return ("contacts", [("contacts", contacts as Any), ("savedCount", savedCount as Any), ("users", users as Any)]) - case .contactsNotModified: - return ("contactsNotModified", []) - } - } - - public static func parse_contacts(_ reader: BufferReader) -> Contacts? { - var _1: [Api.Contact]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Contact.self) - } - var _2: Int32? - _2 = reader.readInt32() - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.contacts.Contacts.contacts(contacts: _1!, savedCount: _2!, users: _3!) - } - else { - return nil - } - } - public static func parse_contactsNotModified(_ reader: BufferReader) -> Contacts? { - return Api.contacts.Contacts.contactsNotModified - } - - } -} -public extension Api.contacts { - enum Found: TypeConstructorDescription { - case found(myResults: [Api.Peer], results: [Api.Peer], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .found(let myResults, let results, let chats, let users): - if boxed { - buffer.appendInt32(-1290580579) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(myResults.count)) - for item in myResults { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(results.count)) - for item in results { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .found(let myResults, let results, let chats, let users): - return ("found", [("myResults", myResults as Any), ("results", results as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_found(_ reader: BufferReader) -> Found? { - var _1: [Api.Peer]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) - } - var _2: [Api.Peer]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) - } - var _3: [Api.Chat]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _4: [Api.User]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.contacts.Found.found(myResults: _1!, results: _2!, chats: _3!, users: _4!) - } - else { - return nil - } - } - - } -} -public extension Api.contacts { - enum ImportedContacts: TypeConstructorDescription { - case importedContacts(imported: [Api.ImportedContact], popularInvites: [Api.PopularContact], retryContacts: [Int64], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .importedContacts(let imported, let popularInvites, let retryContacts, let users): - if boxed { - buffer.appendInt32(2010127419) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(imported.count)) - for item in imported { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(popularInvites.count)) - for item in popularInvites { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(retryContacts.count)) - for item in retryContacts { - serializeInt64(item, buffer: buffer, boxed: false) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .importedContacts(let imported, let popularInvites, let retryContacts, let users): - return ("importedContacts", [("imported", imported as Any), ("popularInvites", popularInvites as Any), ("retryContacts", retryContacts as Any), ("users", users as Any)]) - } - } - - public static func parse_importedContacts(_ reader: BufferReader) -> ImportedContacts? { - var _1: [Api.ImportedContact]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ImportedContact.self) - } - var _2: [Api.PopularContact]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PopularContact.self) - } - var _3: [Int64]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) - } - var _4: [Api.User]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.contacts.ImportedContacts.importedContacts(imported: _1!, popularInvites: _2!, retryContacts: _3!, users: _4!) - } - else { - return nil - } - } - - } -} -public extension Api.contacts { - enum ResolvedPeer: TypeConstructorDescription { - case resolvedPeer(peer: Api.Peer, chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .resolvedPeer(let peer, let chats, let users): - if boxed { - buffer.appendInt32(2131196633) - } - peer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) +public extension Api.auth { + enum Authorization: TypeConstructorDescription { + case authorization(flags: Int32, otherwiseReloginDays: Int32?, tmpSessions: Int32?, futureAuthToken: Buffer?, user: Api.User) + case authorizationSignUpRequired(flags: Int32, termsOfService: Api.help.TermsOfService?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .authorization(let flags, let otherwiseReloginDays, let tmpSessions, let futureAuthToken, let user): + if boxed { + buffer.appendInt32(782418132) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(otherwiseReloginDays!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(tmpSessions!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeBytes(futureAuthToken!, buffer: buffer, boxed: false)} + user.serialize(buffer, true) + break + case .authorizationSignUpRequired(let flags, let termsOfService): + if boxed { + buffer.appendInt32(1148485274) } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {termsOfService!.serialize(buffer, true)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .resolvedPeer(let peer, let chats, let users): - return ("resolvedPeer", [("peer", peer as Any), ("chats", chats as Any), ("users", users as Any)]) + case .authorization(let flags, let otherwiseReloginDays, let tmpSessions, let futureAuthToken, let user): + return ("authorization", [("flags", flags as Any), ("otherwiseReloginDays", otherwiseReloginDays as Any), ("tmpSessions", tmpSessions as Any), ("futureAuthToken", futureAuthToken as Any), ("user", user as Any)]) + case .authorizationSignUpRequired(let flags, let termsOfService): + return ("authorizationSignUpRequired", [("flags", flags as Any), ("termsOfService", termsOfService as Any)]) } } - public static func parse_resolvedPeer(_ reader: BufferReader) -> ResolvedPeer? { - var _1: Api.Peer? + public static func parse_authorization(_ reader: BufferReader) -> Authorization? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() } + var _3: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } + var _4: Buffer? + if Int(_1!) & Int(1 << 2) != 0 {_4 = parseBytes(reader) } + var _5: Api.User? if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Peer + _5 = Api.parse(reader, signature: signature) as? Api.User } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.auth.Authorization.authorization(flags: _1!, otherwiseReloginDays: _2, tmpSessions: _3, futureAuthToken: _4, user: _5!) } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + else { + return nil } + } + public static func parse_authorizationSignUpRequired(_ reader: BufferReader) -> Authorization? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.help.TermsOfService? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.help.TermsOfService + } } let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.contacts.ResolvedPeer.resolvedPeer(peer: _1!, chats: _2!, users: _3!) + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + if _c1 && _c2 { + return Api.auth.Authorization.authorizationSignUpRequired(flags: _1!, termsOfService: _2) } else { return nil @@ -548,43 +78,43 @@ public extension Api.contacts { } } -public extension Api.contacts { - enum TopPeers: TypeConstructorDescription { - case topPeers(categories: [Api.TopPeerCategoryPeers], chats: [Api.Chat], users: [Api.User]) - case topPeersDisabled - case topPeersNotModified +public extension Api.auth { + enum CodeType: TypeConstructorDescription { + case codeTypeCall + case codeTypeFlashCall + case codeTypeFragmentSms + case codeTypeMissedCall + case codeTypeSms public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .topPeers(let categories, let chats, let users): + case .codeTypeCall: if boxed { - buffer.appendInt32(1891070632) + buffer.appendInt32(1948046307) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(categories.count)) - for item in categories { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) + + break + case .codeTypeFlashCall: + if boxed { + buffer.appendInt32(577556219) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + + break + case .codeTypeFragmentSms: + if boxed { + buffer.appendInt32(116234636) } + break - case .topPeersDisabled: + case .codeTypeMissedCall: if boxed { - buffer.appendInt32(-1255369827) + buffer.appendInt32(-702884114) } break - case .topPeersNotModified: + case .codeTypeSms: if boxed { - buffer.appendInt32(-567906571) + buffer.appendInt32(1923290508) } break @@ -593,295 +123,309 @@ public extension Api.contacts { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .topPeers(let categories, let chats, let users): - return ("topPeers", [("categories", categories as Any), ("chats", chats as Any), ("users", users as Any)]) - case .topPeersDisabled: - return ("topPeersDisabled", []) - case .topPeersNotModified: - return ("topPeersNotModified", []) + case .codeTypeCall: + return ("codeTypeCall", []) + case .codeTypeFlashCall: + return ("codeTypeFlashCall", []) + case .codeTypeFragmentSms: + return ("codeTypeFragmentSms", []) + case .codeTypeMissedCall: + return ("codeTypeMissedCall", []) + case .codeTypeSms: + return ("codeTypeSms", []) } } - public static func parse_topPeers(_ reader: BufferReader) -> TopPeers? { - var _1: [Api.TopPeerCategoryPeers]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.TopPeerCategoryPeers.self) - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } + public static func parse_codeTypeCall(_ reader: BufferReader) -> CodeType? { + return Api.auth.CodeType.codeTypeCall + } + public static func parse_codeTypeFlashCall(_ reader: BufferReader) -> CodeType? { + return Api.auth.CodeType.codeTypeFlashCall + } + public static func parse_codeTypeFragmentSms(_ reader: BufferReader) -> CodeType? { + return Api.auth.CodeType.codeTypeFragmentSms + } + public static func parse_codeTypeMissedCall(_ reader: BufferReader) -> CodeType? { + return Api.auth.CodeType.codeTypeMissedCall + } + public static func parse_codeTypeSms(_ reader: BufferReader) -> CodeType? { + return Api.auth.CodeType.codeTypeSms + } + + } +} +public extension Api.auth { + enum ExportedAuthorization: TypeConstructorDescription { + case exportedAuthorization(id: Int64, bytes: Buffer) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .exportedAuthorization(let id, let bytes): + if boxed { + buffer.appendInt32(-1271602504) + } + serializeInt64(id, buffer: buffer, boxed: false) + serializeBytes(bytes, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .exportedAuthorization(let id, let bytes): + return ("exportedAuthorization", [("id", id as Any), ("bytes", bytes as Any)]) + } + } + + public static func parse_exportedAuthorization(_ reader: BufferReader) -> ExportedAuthorization? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Buffer? + _2 = parseBytes(reader) let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.contacts.TopPeers.topPeers(categories: _1!, chats: _2!, users: _3!) + if _c1 && _c2 { + return Api.auth.ExportedAuthorization.exportedAuthorization(id: _1!, bytes: _2!) } else { return nil } } - public static func parse_topPeersDisabled(_ reader: BufferReader) -> TopPeers? { - return Api.contacts.TopPeers.topPeersDisabled - } - public static func parse_topPeersNotModified(_ reader: BufferReader) -> TopPeers? { - return Api.contacts.TopPeers.topPeersNotModified - } } } -public extension Api.help { - enum AppConfig: TypeConstructorDescription { - case appConfig(hash: Int32, config: Api.JSONValue) - case appConfigNotModified +public extension Api.auth { + enum LoggedOut: TypeConstructorDescription { + case loggedOut(flags: Int32, futureAuthToken: Buffer?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .appConfig(let hash, let config): - if boxed { - buffer.appendInt32(-585598930) - } - serializeInt32(hash, buffer: buffer, boxed: false) - config.serialize(buffer, true) - break - case .appConfigNotModified: + case .loggedOut(let flags, let futureAuthToken): if boxed { - buffer.appendInt32(2094949405) + buffer.appendInt32(-1012759713) } - + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeBytes(futureAuthToken!, buffer: buffer, boxed: false)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .appConfig(let hash, let config): - return ("appConfig", [("hash", hash as Any), ("config", config as Any)]) - case .appConfigNotModified: - return ("appConfigNotModified", []) + case .loggedOut(let flags, let futureAuthToken): + return ("loggedOut", [("flags", flags as Any), ("futureAuthToken", futureAuthToken as Any)]) } } - public static func parse_appConfig(_ reader: BufferReader) -> AppConfig? { + public static func parse_loggedOut(_ reader: BufferReader) -> LoggedOut? { var _1: Int32? _1 = reader.readInt32() - var _2: Api.JSONValue? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.JSONValue - } + var _2: Buffer? + if Int(_1!) & Int(1 << 0) != 0 {_2 = parseBytes(reader) } let _c1 = _1 != nil - let _c2 = _2 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil if _c1 && _c2 { - return Api.help.AppConfig.appConfig(hash: _1!, config: _2!) + return Api.auth.LoggedOut.loggedOut(flags: _1!, futureAuthToken: _2) } else { return nil } } - public static func parse_appConfigNotModified(_ reader: BufferReader) -> AppConfig? { - return Api.help.AppConfig.appConfigNotModified - } } } -public extension Api.help { - enum AppUpdate: TypeConstructorDescription { - case appUpdate(flags: Int32, id: Int32, version: String, text: String, entities: [Api.MessageEntity], document: Api.Document?, url: String?, sticker: Api.Document?) - case noAppUpdate +public extension Api.auth { + enum LoginToken: TypeConstructorDescription { + case loginToken(expires: Int32, token: Buffer) + case loginTokenMigrateTo(dcId: Int32, token: Buffer) + case loginTokenSuccess(authorization: Api.auth.Authorization) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .appUpdate(let flags, let id, let version, let text, let entities, let document, let url, let sticker): + case .loginToken(let expires, let token): if boxed { - buffer.appendInt32(-860107216) + buffer.appendInt32(1654593920) } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(id, buffer: buffer, boxed: false) - serializeString(version, buffer: buffer, boxed: false) - serializeString(text, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities.count)) - for item in entities { - item.serialize(buffer, true) + serializeInt32(expires, buffer: buffer, boxed: false) + serializeBytes(token, buffer: buffer, boxed: false) + break + case .loginTokenMigrateTo(let dcId, let token): + if boxed { + buffer.appendInt32(110008598) } - if Int(flags) & Int(1 << 1) != 0 {document!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(url!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {sticker!.serialize(buffer, true)} + serializeInt32(dcId, buffer: buffer, boxed: false) + serializeBytes(token, buffer: buffer, boxed: false) break - case .noAppUpdate: + case .loginTokenSuccess(let authorization): if boxed { - buffer.appendInt32(-1000708810) + buffer.appendInt32(957176926) } - + authorization.serialize(buffer, true) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .appUpdate(let flags, let id, let version, let text, let entities, let document, let url, let sticker): - return ("appUpdate", [("flags", flags as Any), ("id", id as Any), ("version", version as Any), ("text", text as Any), ("entities", entities as Any), ("document", document as Any), ("url", url as Any), ("sticker", sticker as Any)]) - case .noAppUpdate: - return ("noAppUpdate", []) + case .loginToken(let expires, let token): + return ("loginToken", [("expires", expires as Any), ("token", token as Any)]) + case .loginTokenMigrateTo(let dcId, let token): + return ("loginTokenMigrateTo", [("dcId", dcId as Any), ("token", token as Any)]) + case .loginTokenSuccess(let authorization): + return ("loginTokenSuccess", [("authorization", authorization as Any)]) } } - public static func parse_appUpdate(_ reader: BufferReader) -> AppUpdate? { + public static func parse_loginToken(_ reader: BufferReader) -> LoginToken? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: String? - _3 = parseString(reader) - var _4: String? - _4 = parseString(reader) - var _5: [Api.MessageEntity]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) + var _2: Buffer? + _2 = parseBytes(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.auth.LoginToken.loginToken(expires: _1!, token: _2!) } - var _6: Api.Document? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.Document - } } - var _7: String? - if Int(_1!) & Int(1 << 2) != 0 {_7 = parseString(reader) } - var _8: Api.Document? - if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.Document - } } + else { + return nil + } + } + public static func parse_loginTokenMigrateTo(_ reader: BufferReader) -> LoginToken? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Buffer? + _2 = parseBytes(reader) let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil - let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.help.AppUpdate.appUpdate(flags: _1!, id: _2!, version: _3!, text: _4!, entities: _5!, document: _6, url: _7, sticker: _8) + if _c1 && _c2 { + return Api.auth.LoginToken.loginTokenMigrateTo(dcId: _1!, token: _2!) } else { return nil } } - public static func parse_noAppUpdate(_ reader: BufferReader) -> AppUpdate? { - return Api.help.AppUpdate.noAppUpdate + public static func parse_loginTokenSuccess(_ reader: BufferReader) -> LoginToken? { + var _1: Api.auth.Authorization? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.auth.Authorization + } + let _c1 = _1 != nil + if _c1 { + return Api.auth.LoginToken.loginTokenSuccess(authorization: _1!) + } + else { + return nil + } } } } -public extension Api.help { - enum CountriesList: TypeConstructorDescription { - case countriesList(countries: [Api.help.Country], hash: Int32) - case countriesListNotModified +public extension Api.auth { + enum PasswordRecovery: TypeConstructorDescription { + case passwordRecovery(emailPattern: String) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .countriesList(let countries, let hash): + case .passwordRecovery(let emailPattern): if boxed { - buffer.appendInt32(-2016381538) + buffer.appendInt32(326715557) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(countries.count)) - for item in countries { - item.serialize(buffer, true) - } - serializeInt32(hash, buffer: buffer, boxed: false) - break - case .countriesListNotModified: - if boxed { - buffer.appendInt32(-1815339214) - } - + serializeString(emailPattern, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .countriesList(let countries, let hash): - return ("countriesList", [("countries", countries as Any), ("hash", hash as Any)]) - case .countriesListNotModified: - return ("countriesListNotModified", []) + case .passwordRecovery(let emailPattern): + return ("passwordRecovery", [("emailPattern", emailPattern as Any)]) } } - public static func parse_countriesList(_ reader: BufferReader) -> CountriesList? { - var _1: [Api.help.Country]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.help.Country.self) - } - var _2: Int32? - _2 = reader.readInt32() + public static func parse_passwordRecovery(_ reader: BufferReader) -> PasswordRecovery? { + var _1: String? + _1 = parseString(reader) let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.help.CountriesList.countriesList(countries: _1!, hash: _2!) + if _c1 { + return Api.auth.PasswordRecovery.passwordRecovery(emailPattern: _1!) } else { return nil } } - public static func parse_countriesListNotModified(_ reader: BufferReader) -> CountriesList? { - return Api.help.CountriesList.countriesListNotModified - } } } -public extension Api.help { - enum Country: TypeConstructorDescription { - case country(flags: Int32, iso2: String, defaultName: String, name: String?, countryCodes: [Api.help.CountryCode]) +public extension Api.auth { + enum SentCode: TypeConstructorDescription { + case sentCode(flags: Int32, type: Api.auth.SentCodeType, phoneCodeHash: String, nextType: Api.auth.CodeType?, timeout: Int32?) + case sentCodeSuccess(authorization: Api.auth.Authorization) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .country(let flags, let iso2, let defaultName, let name, let countryCodes): + case .sentCode(let flags, let type, let phoneCodeHash, let nextType, let timeout): if boxed { - buffer.appendInt32(-1014526429) + buffer.appendInt32(1577067778) } serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(iso2, buffer: buffer, boxed: false) - serializeString(defaultName, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeString(name!, buffer: buffer, boxed: false)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(countryCodes.count)) - for item in countryCodes { - item.serialize(buffer, true) + type.serialize(buffer, true) + serializeString(phoneCodeHash, buffer: buffer, boxed: false) + 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 .sentCodeSuccess(let authorization): + if boxed { + buffer.appendInt32(596704836) } + authorization.serialize(buffer, true) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .country(let flags, let iso2, let defaultName, let name, let countryCodes): - return ("country", [("flags", flags as Any), ("iso2", iso2 as Any), ("defaultName", defaultName as Any), ("name", name as Any), ("countryCodes", countryCodes as Any)]) + 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 .sentCodeSuccess(let authorization): + return ("sentCodeSuccess", [("authorization", authorization as Any)]) } } - public static func parse_country(_ reader: BufferReader) -> Country? { + public static func parse_sentCode(_ reader: BufferReader) -> SentCode? { var _1: Int32? _1 = reader.readInt32() - var _2: String? - _2 = parseString(reader) + var _2: Api.auth.SentCodeType? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.auth.SentCodeType + } var _3: String? _3 = parseString(reader) - var _4: String? - if Int(_1!) & Int(1 << 1) != 0 {_4 = parseString(reader) } - var _5: [Api.help.CountryCode]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.help.CountryCode.self) - } + var _4: Api.auth.CodeType? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.auth.CodeType + } } + var _5: Int32? + if Int(_1!) & Int(1 << 2) != 0 {_5 = reader.readInt32() } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil - let _c5 = _5 != nil + let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.help.Country.country(flags: _1!, iso2: _2!, defaultName: _3!, name: _4, countryCodes: _5!) + return Api.auth.SentCode.sentCode(flags: _1!, type: _2!, phoneCodeHash: _3!, nextType: _4, timeout: _5) + } + else { + return nil + } + } + public static func parse_sentCodeSuccess(_ reader: BufferReader) -> SentCode? { + var _1: Api.auth.Authorization? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.auth.Authorization + } + let _c1 = _1 != nil + if _c1 { + return Api.auth.SentCode.sentCodeSuccess(authorization: _1!) } else { return nil @@ -890,156 +434,278 @@ public extension Api.help { } } -public extension Api.help { - enum CountryCode: TypeConstructorDescription { - case countryCode(flags: Int32, countryCode: String, prefixes: [String]?, patterns: [String]?) +public extension Api.auth { + enum SentCodeType: TypeConstructorDescription { + case sentCodeTypeApp(length: Int32) + case sentCodeTypeCall(length: Int32) + case sentCodeTypeEmailCode(flags: Int32, emailPattern: String, length: Int32, resetAvailablePeriod: Int32?, resetPendingDate: Int32?) + case sentCodeTypeFirebaseSms(flags: Int32, nonce: Buffer?, receipt: String?, pushTimeout: Int32?, length: Int32) + case sentCodeTypeFlashCall(pattern: String) + case sentCodeTypeFragmentSms(url: String, length: Int32) + case sentCodeTypeMissedCall(prefix: String, length: Int32) + case sentCodeTypeSetUpEmailRequired(flags: Int32) + case sentCodeTypeSms(length: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .countryCode(let flags, let countryCode, let prefixes, let patterns): + case .sentCodeTypeApp(let length): + if boxed { + buffer.appendInt32(1035688326) + } + serializeInt32(length, buffer: buffer, boxed: false) + break + case .sentCodeTypeCall(let length): + if boxed { + buffer.appendInt32(1398007207) + } + serializeInt32(length, buffer: buffer, boxed: false) + break + case .sentCodeTypeEmailCode(let flags, let emailPattern, let length, let resetAvailablePeriod, let resetPendingDate): + if boxed { + buffer.appendInt32(-196020837) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(emailPattern, buffer: buffer, boxed: false) + serializeInt32(length, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 3) != 0 {serializeInt32(resetAvailablePeriod!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {serializeInt32(resetPendingDate!, buffer: buffer, boxed: false)} + break + case .sentCodeTypeFirebaseSms(let flags, let nonce, let receipt, let pushTimeout, let length): + if boxed { + buffer.appendInt32(-444918734) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeBytes(nonce!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(receipt!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(pushTimeout!, buffer: buffer, boxed: false)} + serializeInt32(length, buffer: buffer, boxed: false) + break + case .sentCodeTypeFlashCall(let pattern): + if boxed { + buffer.appendInt32(-1425815847) + } + serializeString(pattern, buffer: buffer, boxed: false) + break + case .sentCodeTypeFragmentSms(let url, let length): if boxed { - buffer.appendInt32(1107543535) + buffer.appendInt32(-648651719) + } + serializeString(url, buffer: buffer, boxed: false) + serializeInt32(length, buffer: buffer, boxed: false) + break + case .sentCodeTypeMissedCall(let prefix, let length): + if boxed { + buffer.appendInt32(-2113903484) + } + serializeString(prefix, buffer: buffer, boxed: false) + serializeInt32(length, buffer: buffer, boxed: false) + break + case .sentCodeTypeSetUpEmailRequired(let flags): + if boxed { + buffer.appendInt32(-1521934870) } serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(countryCode, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(prefixes!.count)) - for item in prefixes! { - serializeString(item, buffer: buffer, boxed: false) - }} - if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(patterns!.count)) - for item in patterns! { - serializeString(item, buffer: buffer, boxed: false) - }} + break + case .sentCodeTypeSms(let length): + if boxed { + buffer.appendInt32(-1073693790) + } + serializeInt32(length, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .countryCode(let flags, let countryCode, let prefixes, let patterns): - return ("countryCode", [("flags", flags as Any), ("countryCode", countryCode as Any), ("prefixes", prefixes as Any), ("patterns", patterns as Any)]) - } - } - - public static func parse_countryCode(_ reader: BufferReader) -> CountryCode? { + case .sentCodeTypeApp(let length): + return ("sentCodeTypeApp", [("length", length as Any)]) + case .sentCodeTypeCall(let length): + return ("sentCodeTypeCall", [("length", length as Any)]) + case .sentCodeTypeEmailCode(let flags, let emailPattern, let length, let resetAvailablePeriod, let resetPendingDate): + return ("sentCodeTypeEmailCode", [("flags", flags as Any), ("emailPattern", emailPattern as Any), ("length", length as Any), ("resetAvailablePeriod", resetAvailablePeriod as Any), ("resetPendingDate", resetPendingDate as Any)]) + case .sentCodeTypeFirebaseSms(let flags, let nonce, let receipt, let pushTimeout, let length): + return ("sentCodeTypeFirebaseSms", [("flags", flags as Any), ("nonce", nonce as Any), ("receipt", receipt as Any), ("pushTimeout", pushTimeout as Any), ("length", length as Any)]) + case .sentCodeTypeFlashCall(let pattern): + return ("sentCodeTypeFlashCall", [("pattern", pattern as Any)]) + case .sentCodeTypeFragmentSms(let url, let length): + return ("sentCodeTypeFragmentSms", [("url", url as Any), ("length", length as Any)]) + case .sentCodeTypeMissedCall(let prefix, let length): + return ("sentCodeTypeMissedCall", [("prefix", prefix as Any), ("length", length as Any)]) + case .sentCodeTypeSetUpEmailRequired(let flags): + return ("sentCodeTypeSetUpEmailRequired", [("flags", flags as Any)]) + case .sentCodeTypeSms(let length): + return ("sentCodeTypeSms", [("length", length as Any)]) + } + } + + public static func parse_sentCodeTypeApp(_ reader: BufferReader) -> SentCodeType? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.auth.SentCodeType.sentCodeTypeApp(length: _1!) + } + else { + return nil + } + } + public static func parse_sentCodeTypeCall(_ reader: BufferReader) -> SentCodeType? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.auth.SentCodeType.sentCodeTypeCall(length: _1!) + } + else { + return nil + } + } + public static func parse_sentCodeTypeEmailCode(_ reader: BufferReader) -> SentCodeType? { var _1: Int32? _1 = reader.readInt32() var _2: String? _2 = parseString(reader) - var _3: [String]? - if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self) - } } - var _4: [String]? - if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self) - } } + 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 << 4) != 0 {_5 = reader.readInt32() } + 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 << 4) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.auth.SentCodeType.sentCodeTypeEmailCode(flags: _1!, emailPattern: _2!, length: _3!, resetAvailablePeriod: _4, resetPendingDate: _5) + } + else { + return nil + } + } + public static func parse_sentCodeTypeFirebaseSms(_ reader: BufferReader) -> SentCodeType? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Buffer? + if Int(_1!) & Int(1 << 0) != 0 {_2 = parseBytes(reader) } + var _3: String? + if Int(_1!) & Int(1 << 1) != 0 {_3 = parseString(reader) } + var _4: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_4 = reader.readInt32() } + var _5: Int32? + _5 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.auth.SentCodeType.sentCodeTypeFirebaseSms(flags: _1!, nonce: _2, receipt: _3, pushTimeout: _4, length: _5!) + } + else { + return nil + } + } + public static func parse_sentCodeTypeFlashCall(_ reader: BufferReader) -> SentCodeType? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.auth.SentCodeType.sentCodeTypeFlashCall(pattern: _1!) + } + else { + return nil + } + } + public static func parse_sentCodeTypeFragmentSms(_ reader: BufferReader) -> SentCodeType? { + var _1: String? + _1 = parseString(reader) + var _2: Int32? + _2 = reader.readInt32() 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 - if _c1 && _c2 && _c3 && _c4 { - return Api.help.CountryCode.countryCode(flags: _1!, countryCode: _2!, prefixes: _3, patterns: _4) + if _c1 && _c2 { + return Api.auth.SentCodeType.sentCodeTypeFragmentSms(url: _1!, length: _2!) } else { return nil } } - - } -} -public extension Api.help { - enum DeepLinkInfo: TypeConstructorDescription { - case deepLinkInfo(flags: Int32, message: String, entities: [Api.MessageEntity]?) - case deepLinkInfoEmpty - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .deepLinkInfo(let flags, let message, let entities): - if boxed { - buffer.appendInt32(1783556146) - } - serializeInt32(flags, 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) - }} - break - case .deepLinkInfoEmpty: - if boxed { - buffer.appendInt32(1722786150) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .deepLinkInfo(let flags, let message, let entities): - return ("deepLinkInfo", [("flags", flags as Any), ("message", message as Any), ("entities", entities as Any)]) - case .deepLinkInfoEmpty: - return ("deepLinkInfoEmpty", []) - } - } - - public static func parse_deepLinkInfo(_ reader: BufferReader) -> DeepLinkInfo? { + public static func parse_sentCodeTypeMissedCall(_ reader: BufferReader) -> SentCodeType? { + 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.auth.SentCodeType.sentCodeTypeMissedCall(prefix: _1!, length: _2!) + } + else { + return nil + } + } + public static func parse_sentCodeTypeSetUpEmailRequired(_ reader: BufferReader) -> SentCodeType? { var _1: Int32? _1 = reader.readInt32() - var _2: String? - _2 = parseString(reader) - var _3: [Api.MessageEntity]? - if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) - } } let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.help.DeepLinkInfo.deepLinkInfo(flags: _1!, message: _2!, entities: _3) + if _c1 { + return Api.auth.SentCodeType.sentCodeTypeSetUpEmailRequired(flags: _1!) } else { return nil } } - public static func parse_deepLinkInfoEmpty(_ reader: BufferReader) -> DeepLinkInfo? { - return Api.help.DeepLinkInfo.deepLinkInfoEmpty + public static func parse_sentCodeTypeSms(_ reader: BufferReader) -> SentCodeType? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.auth.SentCodeType.sentCodeTypeSms(length: _1!) + } + else { + return nil + } } } } -public extension Api.help { - enum InviteText: TypeConstructorDescription { - case inviteText(message: String) +public extension Api.bots { + enum BotInfo: TypeConstructorDescription { + case botInfo(name: String, about: String, description: String) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .inviteText(let message): + case .botInfo(let name, let about, let description): if boxed { - buffer.appendInt32(415997816) + buffer.appendInt32(-391678544) } - serializeString(message, buffer: buffer, boxed: false) + serializeString(name, buffer: buffer, boxed: false) + serializeString(about, buffer: buffer, boxed: false) + serializeString(description, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .inviteText(let message): - return ("inviteText", [("message", message as Any)]) + case .botInfo(let name, let about, let description): + return ("botInfo", [("name", name as Any), ("about", about as Any), ("description", description as Any)]) } } - public static func parse_inviteText(_ reader: BufferReader) -> InviteText? { + public static func parse_botInfo(_ reader: BufferReader) -> BotInfo? { var _1: String? _1 = parseString(reader) + var _2: String? + _2 = parseString(reader) + var _3: String? + _3 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.help.InviteText.inviteText(message: _1!) + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.bots.BotInfo.botInfo(name: _1!, about: _2!, description: _3!) } else { return nil @@ -1048,89 +714,82 @@ public extension Api.help { } } -public extension Api.help { - enum PassportConfig: TypeConstructorDescription { - case passportConfig(hash: Int32, countriesLangs: Api.DataJSON) - case passportConfigNotModified +public extension Api.channels { + enum AdminLogResults: TypeConstructorDescription { + case adminLogResults(events: [Api.ChannelAdminLogEvent], chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .passportConfig(let hash, let countriesLangs): + case .adminLogResults(let events, let chats, let users): if boxed { - buffer.appendInt32(-1600596305) + buffer.appendInt32(-309659827) } - serializeInt32(hash, buffer: buffer, boxed: false) - countriesLangs.serialize(buffer, true) - break - case .passportConfigNotModified: - if boxed { - buffer.appendInt32(-1078332329) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(events.count)) + for item in events { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) } - break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .passportConfig(let hash, let countriesLangs): - return ("passportConfig", [("hash", hash as Any), ("countriesLangs", countriesLangs as Any)]) - case .passportConfigNotModified: - return ("passportConfigNotModified", []) + case .adminLogResults(let events, let chats, let users): + return ("adminLogResults", [("events", events as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_passportConfig(_ reader: BufferReader) -> PassportConfig? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.DataJSON? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.DataJSON + public static func parse_adminLogResults(_ reader: BufferReader) -> AdminLogResults? { + var _1: [Api.ChannelAdminLogEvent]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ChannelAdminLogEvent.self) + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.help.PassportConfig.passportConfig(hash: _1!, countriesLangs: _2!) + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.channels.AdminLogResults.adminLogResults(events: _1!, chats: _2!, users: _3!) } else { return nil } } - public static func parse_passportConfigNotModified(_ reader: BufferReader) -> PassportConfig? { - return Api.help.PassportConfig.passportConfigNotModified - } } } -public extension Api.help { - enum PremiumPromo: TypeConstructorDescription { - case premiumPromo(statusText: String, statusEntities: [Api.MessageEntity], videoSections: [String], videos: [Api.Document], periodOptions: [Api.PremiumSubscriptionOption], users: [Api.User]) +public extension Api.channels { + enum ChannelParticipant: TypeConstructorDescription { + case channelParticipant(participant: Api.ChannelParticipant, chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let periodOptions, let users): + case .channelParticipant(let participant, let chats, let users): if boxed { - buffer.appendInt32(1395946908) - } - serializeString(statusText, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(statusEntities.count)) - for item in statusEntities { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(videoSections.count)) - for item in videoSections { - serializeString(item, buffer: buffer, boxed: false) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(videos.count)) - for item in videos { - item.serialize(buffer, true) + buffer.appendInt32(-541588713) } + participant.serialize(buffer, true) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(periodOptions.count)) - for item in periodOptions { + buffer.appendInt32(Int32(chats.count)) + for item in chats { item.serialize(buffer, true) } buffer.appendInt32(481674261) @@ -1144,42 +803,29 @@ public extension Api.help { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let periodOptions, let users): - return ("premiumPromo", [("statusText", statusText as Any), ("statusEntities", statusEntities as Any), ("videoSections", videoSections as Any), ("videos", videos as Any), ("periodOptions", periodOptions as Any), ("users", users as Any)]) + case .channelParticipant(let participant, let chats, let users): + return ("channelParticipant", [("participant", participant as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_premiumPromo(_ reader: BufferReader) -> PremiumPromo? { - var _1: String? - _1 = parseString(reader) - var _2: [Api.MessageEntity]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) - } - var _3: [String]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self) - } - var _4: [Api.Document]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + public static func parse_channelParticipant(_ reader: BufferReader) -> ChannelParticipant? { + var _1: Api.ChannelParticipant? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.ChannelParticipant } - var _5: [Api.PremiumSubscriptionOption]? + var _2: [Api.Chat]? if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PremiumSubscriptionOption.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) } - var _6: [Api.User]? + var _3: [Api.User]? if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.help.PremiumPromo.premiumPromo(statusText: _1!, statusEntities: _2!, videoSections: _3!, videos: _4!, periodOptions: _5!, users: _6!) + if _c1 && _c2 && _c3 { + return Api.channels.ChannelParticipant.channelParticipant(participant: _1!, chats: _2!, users: _3!) } else { return nil @@ -1188,20 +834,23 @@ public extension Api.help { } } -public extension Api.help { - enum PromoData: TypeConstructorDescription { - case promoData(flags: Int32, expires: Int32, peer: Api.Peer, chats: [Api.Chat], users: [Api.User], psaType: String?, psaMessage: String?) - case promoDataEmpty(expires: Int32) +public extension Api.channels { + enum ChannelParticipants: TypeConstructorDescription { + case channelParticipants(count: Int32, participants: [Api.ChannelParticipant], chats: [Api.Chat], users: [Api.User]) + case channelParticipantsNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .promoData(let flags, let expires, let peer, let chats, let users, let psaType, let psaMessage): + case .channelParticipants(let count, let participants, let chats, let users): if boxed { - buffer.appendInt32(-1942390465) + buffer.appendInt32(-1699676497) + } + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(participants.count)) + for item in participants { + item.serialize(buffer, true) } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(expires, buffer: buffer, boxed: false) - peer.serialize(buffer, true) buffer.appendInt32(481674261) buffer.appendInt32(Int32(chats.count)) for item in chats { @@ -1212,89 +861,70 @@ public extension Api.help { for item in users { item.serialize(buffer, true) } - if Int(flags) & Int(1 << 1) != 0 {serializeString(psaType!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(psaMessage!, buffer: buffer, boxed: false)} break - case .promoDataEmpty(let expires): + case .channelParticipantsNotModified: if boxed { - buffer.appendInt32(-1728664459) + buffer.appendInt32(-266911767) } - serializeInt32(expires, buffer: buffer, boxed: false) + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .promoData(let flags, let expires, let peer, let chats, let users, let psaType, let psaMessage): - return ("promoData", [("flags", flags as Any), ("expires", expires as Any), ("peer", peer as Any), ("chats", chats as Any), ("users", users as Any), ("psaType", psaType as Any), ("psaMessage", psaMessage as Any)]) - case .promoDataEmpty(let expires): - return ("promoDataEmpty", [("expires", expires as Any)]) + case .channelParticipants(let count, let participants, let chats, let users): + return ("channelParticipants", [("count", count as Any), ("participants", participants as Any), ("chats", chats as Any), ("users", users as Any)]) + case .channelParticipantsNotModified: + return ("channelParticipantsNotModified", []) } } - public static func parse_promoData(_ reader: BufferReader) -> PromoData? { + public static func parse_channelParticipants(_ reader: BufferReader) -> ChannelParticipants? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Api.Peer? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.Peer + var _2: [Api.ChannelParticipant]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ChannelParticipant.self) } - var _4: [Api.Chat]? + var _3: [Api.Chat]? if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) } - var _5: [Api.User]? + var _4: [Api.User]? if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } - var _6: String? - if Int(_1!) & Int(1 << 1) != 0 {_6 = parseString(reader) } - var _7: String? - if Int(_1!) & Int(1 << 2) != 0 {_7 = parseString(reader) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.help.PromoData.promoData(flags: _1!, expires: _2!, peer: _3!, chats: _4!, users: _5!, psaType: _6, psaMessage: _7) + if _c1 && _c2 && _c3 && _c4 { + return Api.channels.ChannelParticipants.channelParticipants(count: _1!, participants: _2!, chats: _3!, users: _4!) } else { return nil } } - public static func parse_promoDataEmpty(_ reader: BufferReader) -> PromoData? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.help.PromoData.promoDataEmpty(expires: _1!) - } - else { - return nil - } + public static func parse_channelParticipantsNotModified(_ reader: BufferReader) -> ChannelParticipants? { + return Api.channels.ChannelParticipants.channelParticipantsNotModified } } } -public extension Api.help { - enum RecentMeUrls: TypeConstructorDescription { - case recentMeUrls(urls: [Api.RecentMeUrl], chats: [Api.Chat], users: [Api.User]) +public extension Api.channels { + enum SendAsPeers: TypeConstructorDescription { + case sendAsPeers(peers: [Api.SendAsPeer], chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .recentMeUrls(let urls, let chats, let users): + case .sendAsPeers(let peers, let chats, let users): if boxed { - buffer.appendInt32(235081943) + buffer.appendInt32(-191450938) } buffer.appendInt32(481674261) - buffer.appendInt32(Int32(urls.count)) - for item in urls { + buffer.appendInt32(Int32(peers.count)) + for item in peers { item.serialize(buffer, true) } buffer.appendInt32(481674261) @@ -1313,15 +943,15 @@ public extension Api.help { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .recentMeUrls(let urls, let chats, let users): - return ("recentMeUrls", [("urls", urls as Any), ("chats", chats as Any), ("users", users as Any)]) + case .sendAsPeers(let peers, let chats, let users): + return ("sendAsPeers", [("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_recentMeUrls(_ reader: BufferReader) -> RecentMeUrls? { - var _1: [Api.RecentMeUrl]? + public static func parse_sendAsPeers(_ reader: BufferReader) -> SendAsPeers? { + var _1: [Api.SendAsPeer]? if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RecentMeUrl.self) + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SendAsPeer.self) } var _2: [Api.Chat]? if let _ = reader.readInt32() { @@ -1335,7 +965,7 @@ public extension Api.help { let _c2 = _2 != nil let _c3 = _3 != nil if _c1 && _c2 && _c3 { - return Api.help.RecentMeUrls.recentMeUrls(urls: _1!, chats: _2!, users: _3!) + return Api.channels.SendAsPeers.sendAsPeers(peers: _1!, chats: _2!, users: _3!) } else { return nil @@ -1344,76 +974,132 @@ public extension Api.help { } } -public extension Api.help { - enum Support: TypeConstructorDescription { - case support(phoneNumber: String, user: Api.User) +public extension Api.chatlists { + enum ChatlistInvite: TypeConstructorDescription { + case chatlistInvite(flags: Int32, title: String, emoticon: String?, peers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) + case chatlistInviteAlready(filterId: Int32, missingPeers: [Api.Peer], alreadyPeers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .support(let phoneNumber, let user): + case .chatlistInvite(let flags, let title, let emoticon, let peers, let chats, let users): if boxed { - buffer.appendInt32(398898678) + buffer.appendInt32(500007837) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(emoticon!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peers.count)) + for item in peers { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + case .chatlistInviteAlready(let filterId, let missingPeers, let alreadyPeers, let chats, let users): + if boxed { + buffer.appendInt32(-91752871) + } + serializeInt32(filterId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(missingPeers.count)) + for item in missingPeers { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(alreadyPeers.count)) + for item in alreadyPeers { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) } - serializeString(phoneNumber, buffer: buffer, boxed: false) - user.serialize(buffer, true) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .support(let phoneNumber, let user): - return ("support", [("phoneNumber", phoneNumber as Any), ("user", user as Any)]) + case .chatlistInvite(let flags, let title, let emoticon, let peers, let chats, let users): + return ("chatlistInvite", [("flags", flags as Any), ("title", title as Any), ("emoticon", emoticon as Any), ("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)]) + case .chatlistInviteAlready(let filterId, let missingPeers, let alreadyPeers, let chats, let users): + return ("chatlistInviteAlready", [("filterId", filterId as Any), ("missingPeers", missingPeers as Any), ("alreadyPeers", alreadyPeers as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_support(_ reader: BufferReader) -> Support? { - var _1: String? - _1 = parseString(reader) - var _2: Api.User? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.User + public static func parse_chatlistInvite(_ reader: BufferReader) -> ChatlistInvite? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + var _3: String? + if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } + var _4: [Api.Peer]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) + } + var _5: [Api.Chat]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _6: [Api.User]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.help.Support.support(phoneNumber: _1!, user: _2!) + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.chatlists.ChatlistInvite.chatlistInvite(flags: _1!, title: _2!, emoticon: _3, peers: _4!, chats: _5!, users: _6!) } else { return nil } } - - } -} -public extension Api.help { - enum SupportName: TypeConstructorDescription { - case supportName(name: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .supportName(let name): - if boxed { - buffer.appendInt32(-1945767479) - } - serializeString(name, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .supportName(let name): - return ("supportName", [("name", name as Any)]) - } - } - - public static func parse_supportName(_ reader: BufferReader) -> SupportName? { - var _1: String? - _1 = parseString(reader) + public static func parse_chatlistInviteAlready(_ reader: BufferReader) -> ChatlistInvite? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.Peer]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) + } + var _3: [Api.Peer]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) + } + var _4: [Api.Chat]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _5: [Api.User]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } let _c1 = _1 != nil - if _c1 { - return Api.help.SupportName.supportName(name: _1!) + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.chatlists.ChatlistInvite.chatlistInviteAlready(filterId: _1!, missingPeers: _2!, alreadyPeers: _3!, chats: _4!, users: _5!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api26.swift b/submodules/TelegramApi/Sources/Api26.swift index 803213f1dd9..585befa4ad5 100644 --- a/submodules/TelegramApi/Sources/Api26.swift +++ b/submodules/TelegramApi/Sources/Api26.swift @@ -1,55 +1,57 @@ -public extension Api.help { - enum TermsOfService: TypeConstructorDescription { - case termsOfService(flags: Int32, id: Api.DataJSON, text: String, entities: [Api.MessageEntity], minAgeConfirm: Int32?) +public extension Api.chatlists { + enum ChatlistUpdates: TypeConstructorDescription { + case chatlistUpdates(missingPeers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .termsOfService(let flags, let id, let text, let entities, let minAgeConfirm): + case .chatlistUpdates(let missingPeers, let chats, let users): if boxed { - buffer.appendInt32(2013922064) + buffer.appendInt32(-1816295539) } - serializeInt32(flags, buffer: buffer, boxed: false) - id.serialize(buffer, true) - serializeString(text, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities.count)) - for item in entities { + buffer.appendInt32(Int32(missingPeers.count)) + for item in missingPeers { + 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 << 1) != 0 {serializeInt32(minAgeConfirm!, buffer: buffer, boxed: false)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .termsOfService(let flags, let id, let text, let entities, let minAgeConfirm): - return ("termsOfService", [("flags", flags as Any), ("id", id as Any), ("text", text as Any), ("entities", entities as Any), ("minAgeConfirm", minAgeConfirm as Any)]) + case .chatlistUpdates(let missingPeers, let chats, let users): + return ("chatlistUpdates", [("missingPeers", missingPeers as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_termsOfService(_ reader: BufferReader) -> TermsOfService? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.DataJSON? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.DataJSON + public static func parse_chatlistUpdates(_ reader: BufferReader) -> ChatlistUpdates? { + var _1: [Api.Peer]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) } - var _3: String? - _3 = parseString(reader) - var _4: [Api.MessageEntity]? + var _2: [Api.Chat]? if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.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) } - var _5: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() } let _c1 = _1 != nil let _c2 = _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.help.TermsOfService.termsOfService(flags: _1!, id: _2!, text: _3!, entities: _4!, minAgeConfirm: _5) + if _c1 && _c2 && _c3 { + return Api.chatlists.ChatlistUpdates.chatlistUpdates(missingPeers: _1!, chats: _2!, users: _3!) } else { return nil @@ -58,60 +60,42 @@ public extension Api.help { } } -public extension Api.help { - enum TermsOfServiceUpdate: TypeConstructorDescription { - case termsOfServiceUpdate(expires: Int32, termsOfService: Api.help.TermsOfService) - case termsOfServiceUpdateEmpty(expires: Int32) +public extension Api.chatlists { + enum ExportedChatlistInvite: TypeConstructorDescription { + case exportedChatlistInvite(filter: Api.DialogFilter, invite: Api.ExportedChatlistInvite) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .termsOfServiceUpdate(let expires, let termsOfService): - if boxed { - buffer.appendInt32(686618977) - } - serializeInt32(expires, buffer: buffer, boxed: false) - termsOfService.serialize(buffer, true) - break - case .termsOfServiceUpdateEmpty(let expires): + case .exportedChatlistInvite(let filter, let invite): if boxed { - buffer.appendInt32(-483352705) + buffer.appendInt32(283567014) } - serializeInt32(expires, buffer: buffer, boxed: false) + filter.serialize(buffer, true) + invite.serialize(buffer, true) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .termsOfServiceUpdate(let expires, let termsOfService): - return ("termsOfServiceUpdate", [("expires", expires as Any), ("termsOfService", termsOfService as Any)]) - case .termsOfServiceUpdateEmpty(let expires): - return ("termsOfServiceUpdateEmpty", [("expires", expires as Any)]) + case .exportedChatlistInvite(let filter, let invite): + return ("exportedChatlistInvite", [("filter", filter as Any), ("invite", invite as Any)]) } } - public static func parse_termsOfServiceUpdate(_ reader: BufferReader) -> TermsOfServiceUpdate? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.help.TermsOfService? + public static func parse_exportedChatlistInvite(_ reader: BufferReader) -> ExportedChatlistInvite? { + var _1: Api.DialogFilter? if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.help.TermsOfService + _1 = Api.parse(reader, signature: signature) as? Api.DialogFilter + } + var _2: Api.ExportedChatlistInvite? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.ExportedChatlistInvite } let _c1 = _1 != nil let _c2 = _2 != nil if _c1 && _c2 { - return Api.help.TermsOfServiceUpdate.termsOfServiceUpdate(expires: _1!, termsOfService: _2!) - } - else { - return nil - } - } - public static func parse_termsOfServiceUpdateEmpty(_ reader: BufferReader) -> TermsOfServiceUpdate? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.help.TermsOfServiceUpdate.termsOfServiceUpdateEmpty(expires: _1!) + return Api.chatlists.ExportedChatlistInvite.exportedChatlistInvite(filter: _1!, invite: _2!) } else { return nil @@ -120,89 +104,114 @@ public extension Api.help { } } -public extension Api.help { - enum UserInfo: TypeConstructorDescription { - case userInfo(message: String, entities: [Api.MessageEntity], author: String, date: Int32) - case userInfoEmpty +public extension Api.chatlists { + enum ExportedInvites: TypeConstructorDescription { + case exportedInvites(invites: [Api.ExportedChatlistInvite], chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .userInfo(let message, let entities, let author, let date): + case .exportedInvites(let invites, let chats, let users): if boxed { - buffer.appendInt32(32192344) + buffer.appendInt32(279670215) } - serializeString(message, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities.count)) - for item in entities { + buffer.appendInt32(Int32(invites.count)) + for item in invites { item.serialize(buffer, true) } - serializeString(author, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - break - case .userInfoEmpty: - if boxed { - buffer.appendInt32(-206688531) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) } - break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .userInfo(let message, let entities, let author, let date): - return ("userInfo", [("message", message as Any), ("entities", entities as Any), ("author", author as Any), ("date", date as Any)]) - case .userInfoEmpty: - return ("userInfoEmpty", []) + case .exportedInvites(let invites, let chats, let users): + return ("exportedInvites", [("invites", invites as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_userInfo(_ reader: BufferReader) -> UserInfo? { - var _1: String? - _1 = parseString(reader) - var _2: [Api.MessageEntity]? + public static func parse_exportedInvites(_ reader: BufferReader) -> ExportedInvites? { + var _1: [Api.ExportedChatlistInvite]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ExportedChatlistInvite.self) + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } - var _3: String? - _3 = parseString(reader) - 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.help.UserInfo.userInfo(message: _1!, entities: _2!, author: _3!, date: _4!) + if _c1 && _c2 && _c3 { + return Api.chatlists.ExportedInvites.exportedInvites(invites: _1!, chats: _2!, users: _3!) } else { return nil } } - public static func parse_userInfoEmpty(_ reader: BufferReader) -> UserInfo? { - return Api.help.UserInfo.userInfoEmpty - } } } -public extension Api.messages { - enum AffectedFoundMessages: TypeConstructorDescription { - case affectedFoundMessages(pts: Int32, ptsCount: Int32, offset: Int32, messages: [Int32]) +public extension Api.contacts { + enum Blocked: TypeConstructorDescription { + case blocked(blocked: [Api.PeerBlocked], chats: [Api.Chat], users: [Api.User]) + case blockedSlice(count: Int32, blocked: [Api.PeerBlocked], chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .affectedFoundMessages(let pts, let ptsCount, let offset, let messages): + case .blocked(let blocked, let chats, let users): if boxed { - buffer.appendInt32(-275956116) + buffer.appendInt32(182326673) } - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - serializeInt32(offset, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - serializeInt32(item, buffer: buffer, boxed: false) + buffer.appendInt32(Int32(blocked.count)) + for item in blocked { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + case .blockedSlice(let count, let blocked, let chats, let users): + if boxed { + buffer.appendInt32(-513392236) + } + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(blocked.count)) + for item in blocked { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) } break } @@ -210,28 +219,57 @@ public extension Api.messages { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .affectedFoundMessages(let pts, let ptsCount, let offset, let messages): - return ("affectedFoundMessages", [("pts", pts as Any), ("ptsCount", ptsCount as Any), ("offset", offset as Any), ("messages", messages as Any)]) + case .blocked(let blocked, let chats, let users): + return ("blocked", [("blocked", blocked as Any), ("chats", chats as Any), ("users", users as Any)]) + case .blockedSlice(let count, let blocked, let chats, let users): + return ("blockedSlice", [("count", count as Any), ("blocked", blocked as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_affectedFoundMessages(_ reader: BufferReader) -> AffectedFoundMessages? { + public static func parse_blocked(_ reader: BufferReader) -> Blocked? { + var _1: [Api.PeerBlocked]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PeerBlocked.self) + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.contacts.Blocked.blocked(blocked: _1!, chats: _2!, users: _3!) + } + else { + return nil + } + } + public static func parse_blockedSlice(_ reader: BufferReader) -> Blocked? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() - var _4: [Int32]? + var _2: [Api.PeerBlocked]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PeerBlocked.self) + } + var _3: [Api.Chat]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _4: [Api.User]? if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil if _c1 && _c2 && _c3 && _c4 { - return Api.messages.AffectedFoundMessages.affectedFoundMessages(pts: _1!, ptsCount: _2!, offset: _3!, messages: _4!) + return Api.contacts.Blocked.blockedSlice(count: _1!, blocked: _2!, chats: _3!, users: _4!) } else { return nil @@ -240,82 +278,138 @@ public extension Api.messages { } } -public extension Api.messages { - enum AffectedHistory: TypeConstructorDescription { - case affectedHistory(pts: Int32, ptsCount: Int32, offset: Int32) +public extension Api.contacts { + enum Contacts: TypeConstructorDescription { + case contacts(contacts: [Api.Contact], savedCount: Int32, users: [Api.User]) + case contactsNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .affectedHistory(let pts, let ptsCount, let offset): + case .contacts(let contacts, let savedCount, let users): + if boxed { + buffer.appendInt32(-353862078) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(contacts.count)) + for item in contacts { + item.serialize(buffer, true) + } + serializeInt32(savedCount, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + case .contactsNotModified: if boxed { - buffer.appendInt32(-1269012015) + buffer.appendInt32(-1219778094) } - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) - serializeInt32(offset, buffer: buffer, boxed: false) + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .affectedHistory(let pts, let ptsCount, let offset): - return ("affectedHistory", [("pts", pts as Any), ("ptsCount", ptsCount as Any), ("offset", offset as Any)]) + case .contacts(let contacts, let savedCount, let users): + return ("contacts", [("contacts", contacts as Any), ("savedCount", savedCount as Any), ("users", users as Any)]) + case .contactsNotModified: + return ("contactsNotModified", []) } } - public static func parse_affectedHistory(_ reader: BufferReader) -> AffectedHistory? { - var _1: Int32? - _1 = reader.readInt32() + public static func parse_contacts(_ reader: BufferReader) -> Contacts? { + var _1: [Api.Contact]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Contact.self) + } var _2: Int32? _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil if _c1 && _c2 && _c3 { - return Api.messages.AffectedHistory.affectedHistory(pts: _1!, ptsCount: _2!, offset: _3!) + return Api.contacts.Contacts.contacts(contacts: _1!, savedCount: _2!, users: _3!) } else { return nil } } + public static func parse_contactsNotModified(_ reader: BufferReader) -> Contacts? { + return Api.contacts.Contacts.contactsNotModified + } } } -public extension Api.messages { - enum AffectedMessages: TypeConstructorDescription { - case affectedMessages(pts: Int32, ptsCount: Int32) +public extension Api.contacts { + enum Found: TypeConstructorDescription { + case found(myResults: [Api.Peer], results: [Api.Peer], chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .affectedMessages(let pts, let ptsCount): + case .found(let myResults, let results, let chats, let users): if boxed { - buffer.appendInt32(-2066640507) + buffer.appendInt32(-1290580579) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(myResults.count)) + for item in myResults { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(results.count)) + for item in results { + 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) } - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(ptsCount, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .affectedMessages(let pts, let ptsCount): - return ("affectedMessages", [("pts", pts as Any), ("ptsCount", ptsCount as Any)]) + case .found(let myResults, let results, let chats, let users): + return ("found", [("myResults", myResults as Any), ("results", results as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_affectedMessages(_ reader: BufferReader) -> AffectedMessages? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() + public static func parse_found(_ reader: BufferReader) -> Found? { + var _1: [Api.Peer]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) + } + var _2: [Api.Peer]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) + } + var _3: [Api.Chat]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _4: [Api.User]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.AffectedMessages.affectedMessages(pts: _1!, ptsCount: _2!) + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.contacts.Found.found(myResults: _1!, results: _2!, chats: _3!, users: _4!) } else { return nil @@ -324,78 +418,97 @@ public extension Api.messages { } } -public extension Api.messages { - enum AllStickers: TypeConstructorDescription { - case allStickers(hash: Int64, sets: [Api.StickerSet]) - case allStickersNotModified +public extension Api.contacts { + enum ImportedContacts: TypeConstructorDescription { + case importedContacts(imported: [Api.ImportedContact], popularInvites: [Api.PopularContact], retryContacts: [Int64], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .allStickers(let hash, let sets): + case .importedContacts(let imported, let popularInvites, let retryContacts, let users): if boxed { - buffer.appendInt32(-843329861) + buffer.appendInt32(2010127419) } - serializeInt64(hash, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(sets.count)) - for item in sets { + buffer.appendInt32(Int32(imported.count)) + for item in imported { item.serialize(buffer, true) } - break - case .allStickersNotModified: - if boxed { - buffer.appendInt32(-395967805) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(popularInvites.count)) + for item in popularInvites { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(retryContacts.count)) + for item in retryContacts { + serializeInt64(item, buffer: buffer, boxed: false) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) } - break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .allStickers(let hash, let sets): - return ("allStickers", [("hash", hash as Any), ("sets", sets as Any)]) - case .allStickersNotModified: - return ("allStickersNotModified", []) + case .importedContacts(let imported, let popularInvites, let retryContacts, let users): + return ("importedContacts", [("imported", imported as Any), ("popularInvites", popularInvites as Any), ("retryContacts", retryContacts as Any), ("users", users as Any)]) } } - public static func parse_allStickers(_ reader: BufferReader) -> AllStickers? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.StickerSet]? + public static func parse_importedContacts(_ reader: BufferReader) -> ImportedContacts? { + var _1: [Api.ImportedContact]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ImportedContact.self) + } + var _2: [Api.PopularContact]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PopularContact.self) + } + var _3: [Int64]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } + var _4: [Api.User]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSet.self) + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.AllStickers.allStickers(hash: _1!, sets: _2!) + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.contacts.ImportedContacts.importedContacts(imported: _1!, popularInvites: _2!, retryContacts: _3!, users: _4!) } else { return nil } } - public static func parse_allStickersNotModified(_ reader: BufferReader) -> AllStickers? { - return Api.messages.AllStickers.allStickersNotModified - } } } -public extension Api.messages { - enum ArchivedStickers: TypeConstructorDescription { - case archivedStickers(count: Int32, sets: [Api.StickerSetCovered]) +public extension Api.contacts { + enum ResolvedPeer: TypeConstructorDescription { + case resolvedPeer(peer: Api.Peer, chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .archivedStickers(let count, let sets): + case .resolvedPeer(let peer, let chats, let users): if boxed { - buffer.appendInt32(1338747336) + buffer.appendInt32(2131196633) } - serializeInt32(count, buffer: buffer, boxed: false) + peer.serialize(buffer, true) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(sets.count)) - for item in sets { + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { item.serialize(buffer, true) } break @@ -404,22 +517,29 @@ public extension Api.messages { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .archivedStickers(let count, let sets): - return ("archivedStickers", [("count", count as Any), ("sets", sets as Any)]) + case .resolvedPeer(let peer, let chats, let users): + return ("resolvedPeer", [("peer", peer as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_archivedStickers(_ reader: BufferReader) -> ArchivedStickers? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.StickerSetCovered]? + public static func parse_resolvedPeer(_ reader: BufferReader) -> ResolvedPeer? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self) + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.ArchivedStickers.archivedStickers(count: _1!, sets: _2!) + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.contacts.ResolvedPeer.resolvedPeer(peer: _1!, chats: _2!, users: _3!) } else { return nil @@ -428,27 +548,43 @@ public extension Api.messages { } } -public extension Api.messages { - enum AvailableReactions: TypeConstructorDescription { - case availableReactions(hash: Int32, reactions: [Api.AvailableReaction]) - case availableReactionsNotModified +public extension Api.contacts { + enum TopPeers: TypeConstructorDescription { + case topPeers(categories: [Api.TopPeerCategoryPeers], chats: [Api.Chat], users: [Api.User]) + case topPeersDisabled + case topPeersNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .availableReactions(let hash, let reactions): + case .topPeers(let categories, let chats, let users): if boxed { - buffer.appendInt32(1989032621) + buffer.appendInt32(1891070632) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(categories.count)) + for item in categories { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) } - serializeInt32(hash, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(reactions.count)) - for item in reactions { + buffer.appendInt32(Int32(users.count)) + for item in users { item.serialize(buffer, true) } break - case .availableReactionsNotModified: + case .topPeersDisabled: if boxed { - buffer.appendInt32(-1626924713) + buffer.appendInt32(-1255369827) + } + + break + case .topPeersNotModified: + if boxed { + buffer.appendInt32(-567906571) } break @@ -457,276 +593,262 @@ public extension Api.messages { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .availableReactions(let hash, let reactions): - return ("availableReactions", [("hash", hash as Any), ("reactions", reactions as Any)]) - case .availableReactionsNotModified: - return ("availableReactionsNotModified", []) + case .topPeers(let categories, let chats, let users): + return ("topPeers", [("categories", categories as Any), ("chats", chats as Any), ("users", users as Any)]) + case .topPeersDisabled: + return ("topPeersDisabled", []) + case .topPeersNotModified: + return ("topPeersNotModified", []) } } - public static func parse_availableReactions(_ reader: BufferReader) -> AvailableReactions? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.AvailableReaction]? + public static func parse_topPeers(_ reader: BufferReader) -> TopPeers? { + var _1: [Api.TopPeerCategoryPeers]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.TopPeerCategoryPeers.self) + } + var _2: [Api.Chat]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.AvailableReaction.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.AvailableReactions.availableReactions(hash: _1!, reactions: _2!) + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.contacts.TopPeers.topPeers(categories: _1!, chats: _2!, users: _3!) } else { return nil } } - public static func parse_availableReactionsNotModified(_ reader: BufferReader) -> AvailableReactions? { - return Api.messages.AvailableReactions.availableReactionsNotModified + public static func parse_topPeersDisabled(_ reader: BufferReader) -> TopPeers? { + return Api.contacts.TopPeers.topPeersDisabled + } + public static func parse_topPeersNotModified(_ reader: BufferReader) -> TopPeers? { + return Api.contacts.TopPeers.topPeersNotModified } } } -public extension Api.messages { - enum BotApp: TypeConstructorDescription { - case botApp(flags: Int32, app: Api.BotApp) +public extension Api.help { + enum AppConfig: TypeConstructorDescription { + case appConfig(hash: Int32, config: Api.JSONValue) + case appConfigNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .botApp(let flags, let app): + case .appConfig(let hash, let config): if boxed { - buffer.appendInt32(-347034123) + buffer.appendInt32(-585598930) } - serializeInt32(flags, buffer: buffer, boxed: false) - app.serialize(buffer, true) + serializeInt32(hash, buffer: buffer, boxed: false) + config.serialize(buffer, true) + break + case .appConfigNotModified: + if boxed { + buffer.appendInt32(2094949405) + } + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .botApp(let flags, let app): - return ("botApp", [("flags", flags as Any), ("app", app as Any)]) + case .appConfig(let hash, let config): + return ("appConfig", [("hash", hash as Any), ("config", config as Any)]) + case .appConfigNotModified: + return ("appConfigNotModified", []) } } - public static func parse_botApp(_ reader: BufferReader) -> BotApp? { + public static func parse_appConfig(_ reader: BufferReader) -> AppConfig? { var _1: Int32? _1 = reader.readInt32() - var _2: Api.BotApp? + var _2: Api.JSONValue? if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.BotApp + _2 = Api.parse(reader, signature: signature) as? Api.JSONValue } let _c1 = _1 != nil let _c2 = _2 != nil if _c1 && _c2 { - return Api.messages.BotApp.botApp(flags: _1!, app: _2!) + return Api.help.AppConfig.appConfig(hash: _1!, config: _2!) } else { return nil } } - - } -} -public extension Api.messages { - enum BotCallbackAnswer: TypeConstructorDescription { - case botCallbackAnswer(flags: Int32, message: String?, url: String?, cacheTime: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .botCallbackAnswer(let flags, let message, let url, let cacheTime): - if boxed { - buffer.appendInt32(911761060) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(message!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(url!, buffer: buffer, boxed: false)} - serializeInt32(cacheTime, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .botCallbackAnswer(let flags, let message, let url, let cacheTime): - return ("botCallbackAnswer", [("flags", flags as Any), ("message", message as Any), ("url", url as Any), ("cacheTime", cacheTime as Any)]) - } - } - - public static func parse_botCallbackAnswer(_ reader: BufferReader) -> BotCallbackAnswer? { - var _1: Int32? - _1 = reader.readInt32() - var _2: String? - if Int(_1!) & Int(1 << 0) != 0 {_2 = parseString(reader) } - var _3: String? - if Int(_1!) & Int(1 << 2) != 0 {_3 = parseString(reader) } - var _4: Int32? - _4 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.BotCallbackAnswer.botCallbackAnswer(flags: _1!, message: _2, url: _3, cacheTime: _4!) - } - else { - return nil - } + public static func parse_appConfigNotModified(_ reader: BufferReader) -> AppConfig? { + return Api.help.AppConfig.appConfigNotModified } } } -public extension Api.messages { - enum BotResults: TypeConstructorDescription { - case botResults(flags: Int32, queryId: Int64, nextOffset: String?, switchPm: Api.InlineBotSwitchPM?, switchWebview: Api.InlineBotWebView?, results: [Api.BotInlineResult], cacheTime: Int32, users: [Api.User]) +public extension Api.help { + enum AppUpdate: TypeConstructorDescription { + case appUpdate(flags: Int32, id: Int32, version: String, text: String, entities: [Api.MessageEntity], document: Api.Document?, url: String?, sticker: Api.Document?) + case noAppUpdate public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .botResults(let flags, let queryId, let nextOffset, let switchPm, let switchWebview, let results, let cacheTime, let users): + case .appUpdate(let flags, let id, let version, let text, let entities, let document, let url, let sticker): if boxed { - buffer.appendInt32(-534646026) + buffer.appendInt32(-860107216) } serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(queryId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {switchPm!.serialize(buffer, true)} - if Int(flags) & Int(1 << 3) != 0 {switchWebview!.serialize(buffer, true)} + serializeInt32(id, buffer: buffer, boxed: false) + serializeString(version, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(results.count)) - for item in results { + buffer.appendInt32(Int32(entities.count)) + for item in entities { item.serialize(buffer, true) } - serializeInt32(cacheTime, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + if Int(flags) & Int(1 << 1) != 0 {document!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(url!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {sticker!.serialize(buffer, true)} + break + case .noAppUpdate: + if boxed { + buffer.appendInt32(-1000708810) } + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .botResults(let flags, let queryId, let nextOffset, let switchPm, let switchWebview, let results, let cacheTime, let users): - return ("botResults", [("flags", flags as Any), ("queryId", queryId as Any), ("nextOffset", nextOffset as Any), ("switchPm", switchPm as Any), ("switchWebview", switchWebview as Any), ("results", results as Any), ("cacheTime", cacheTime as Any), ("users", users as Any)]) + case .appUpdate(let flags, let id, let version, let text, let entities, let document, let url, let sticker): + return ("appUpdate", [("flags", flags as Any), ("id", id as Any), ("version", version as Any), ("text", text as Any), ("entities", entities as Any), ("document", document as Any), ("url", url as Any), ("sticker", sticker as Any)]) + case .noAppUpdate: + return ("noAppUpdate", []) } } - public static func parse_botResults(_ reader: BufferReader) -> BotResults? { + public static func parse_appUpdate(_ reader: BufferReader) -> AppUpdate? { var _1: Int32? _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() + var _2: Int32? + _2 = reader.readInt32() var _3: String? - if Int(_1!) & Int(1 << 1) != 0 {_3 = parseString(reader) } - var _4: Api.InlineBotSwitchPM? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.InlineBotSwitchPM + _3 = parseString(reader) + var _4: String? + _4 = parseString(reader) + var _5: [Api.MessageEntity]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) + } + var _6: Api.Document? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.Document } } - var _5: Api.InlineBotWebView? + var _7: String? + if Int(_1!) & Int(1 << 2) != 0 {_7 = parseString(reader) } + var _8: Api.Document? if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.InlineBotWebView + _8 = Api.parse(reader, signature: signature) as? Api.Document } } - var _6: [Api.BotInlineResult]? - if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.BotInlineResult.self) - } - var _7: Int32? - _7 = reader.readInt32() - var _8: [Api.User]? - if let _ = reader.readInt32() { - _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - let _c8 = _8 != 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 << 2) == 0) || _7 != nil + let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.messages.BotResults.botResults(flags: _1!, queryId: _2!, nextOffset: _3, switchPm: _4, switchWebview: _5, results: _6!, cacheTime: _7!, users: _8!) + return Api.help.AppUpdate.appUpdate(flags: _1!, id: _2!, version: _3!, text: _4!, entities: _5!, document: _6, url: _7, sticker: _8) } else { return nil } } + public static func parse_noAppUpdate(_ reader: BufferReader) -> AppUpdate? { + return Api.help.AppUpdate.noAppUpdate + } } } -public extension Api.messages { - enum ChatAdminsWithInvites: TypeConstructorDescription { - case chatAdminsWithInvites(admins: [Api.ChatAdminWithInvites], users: [Api.User]) +public extension Api.help { + enum CountriesList: TypeConstructorDescription { + case countriesList(countries: [Api.help.Country], hash: Int32) + case countriesListNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .chatAdminsWithInvites(let admins, let users): + case .countriesList(let countries, let hash): if boxed { - buffer.appendInt32(-1231326505) + buffer.appendInt32(-2016381538) } buffer.appendInt32(481674261) - buffer.appendInt32(Int32(admins.count)) - for item in admins { + buffer.appendInt32(Int32(countries.count)) + for item in countries { item.serialize(buffer, true) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + serializeInt32(hash, buffer: buffer, boxed: false) + break + case .countriesListNotModified: + if boxed { + buffer.appendInt32(-1815339214) } + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .chatAdminsWithInvites(let admins, let users): - return ("chatAdminsWithInvites", [("admins", admins as Any), ("users", users as Any)]) + case .countriesList(let countries, let hash): + return ("countriesList", [("countries", countries as Any), ("hash", hash as Any)]) + case .countriesListNotModified: + return ("countriesListNotModified", []) } } - public static func parse_chatAdminsWithInvites(_ reader: BufferReader) -> ChatAdminsWithInvites? { - var _1: [Api.ChatAdminWithInvites]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ChatAdminWithInvites.self) - } - var _2: [Api.User]? + public static func parse_countriesList(_ reader: BufferReader) -> CountriesList? { + var _1: [Api.help.Country]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.help.Country.self) } + var _2: Int32? + _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil if _c1 && _c2 { - return Api.messages.ChatAdminsWithInvites.chatAdminsWithInvites(admins: _1!, users: _2!) + return Api.help.CountriesList.countriesList(countries: _1!, hash: _2!) } else { return nil } } + public static func parse_countriesListNotModified(_ reader: BufferReader) -> CountriesList? { + return Api.help.CountriesList.countriesListNotModified + } } } -public extension Api.messages { - enum ChatFull: TypeConstructorDescription { - case chatFull(fullChat: Api.ChatFull, chats: [Api.Chat], users: [Api.User]) +public extension Api.help { + enum Country: TypeConstructorDescription { + case country(flags: Int32, iso2: String, defaultName: String, name: String?, countryCodes: [Api.help.CountryCode]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .chatFull(let fullChat, let chats, let users): + case .country(let flags, let iso2, let defaultName, let name, let countryCodes): if boxed { - buffer.appendInt32(-438840932) - } - fullChat.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) + buffer.appendInt32(-1014526429) } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(iso2, buffer: buffer, boxed: false) + serializeString(defaultName, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeString(name!, buffer: buffer, boxed: false)} buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { + buffer.appendInt32(Int32(countryCodes.count)) + for item in countryCodes { item.serialize(buffer, true) } break @@ -735,29 +857,31 @@ public extension Api.messages { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .chatFull(let fullChat, let chats, let users): - return ("chatFull", [("fullChat", fullChat as Any), ("chats", chats as Any), ("users", users as Any)]) + case .country(let flags, let iso2, let defaultName, let name, let countryCodes): + return ("country", [("flags", flags as Any), ("iso2", iso2 as Any), ("defaultName", defaultName as Any), ("name", name as Any), ("countryCodes", countryCodes as Any)]) } } - - public static func parse_chatFull(_ reader: BufferReader) -> ChatFull? { - var _1: Api.ChatFull? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.ChatFull - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? + + public static func parse_country(_ reader: BufferReader) -> Country? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + var _3: String? + _3 = parseString(reader) + var _4: String? + if Int(_1!) & Int(1 << 1) != 0 {_4 = parseString(reader) } + var _5: [Api.help.CountryCode]? if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.help.CountryCode.self) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.ChatFull.chatFull(fullChat: _1!, chats: _2!, users: _3!) + let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.help.Country.country(flags: _1!, iso2: _2!, defaultName: _3!, name: _4, countryCodes: _5!) } else { return nil @@ -766,54 +890,58 @@ public extension Api.messages { } } -public extension Api.messages { - enum ChatInviteImporters: TypeConstructorDescription { - case chatInviteImporters(count: Int32, importers: [Api.ChatInviteImporter], users: [Api.User]) +public extension Api.help { + enum CountryCode: TypeConstructorDescription { + case countryCode(flags: Int32, countryCode: String, prefixes: [String]?, patterns: [String]?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .chatInviteImporters(let count, let importers, let users): + case .countryCode(let flags, let countryCode, let prefixes, let patterns): if boxed { - buffer.appendInt32(-2118733814) - } - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(importers.count)) - for item in importers { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + buffer.appendInt32(1107543535) } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(countryCode, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(prefixes!.count)) + for item in prefixes! { + serializeString(item, buffer: buffer, boxed: false) + }} + if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(patterns!.count)) + for item in patterns! { + serializeString(item, buffer: buffer, boxed: false) + }} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .chatInviteImporters(let count, let importers, let users): - return ("chatInviteImporters", [("count", count as Any), ("importers", importers as Any), ("users", users as Any)]) + case .countryCode(let flags, let countryCode, let prefixes, let patterns): + return ("countryCode", [("flags", flags as Any), ("countryCode", countryCode as Any), ("prefixes", prefixes as Any), ("patterns", patterns as Any)]) } } - public static func parse_chatInviteImporters(_ reader: BufferReader) -> ChatInviteImporters? { + public static func parse_countryCode(_ reader: BufferReader) -> CountryCode? { var _1: Int32? _1 = reader.readInt32() - var _2: [Api.ChatInviteImporter]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ChatInviteImporter.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } + var _2: String? + _2 = parseString(reader) + var _3: [String]? + if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self) + } } + var _4: [String]? + if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self) + } } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.ChatInviteImporters.chatInviteImporters(count: _1!, importers: _2!, users: _3!) + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.help.CountryCode.countryCode(flags: _1!, countryCode: _2!, prefixes: _3, patterns: _4) } else { return nil @@ -822,106 +950,96 @@ public extension Api.messages { } } -public extension Api.messages { - enum Chats: TypeConstructorDescription { - case chats(chats: [Api.Chat]) - case chatsSlice(count: Int32, chats: [Api.Chat]) +public extension Api.help { + enum DeepLinkInfo: TypeConstructorDescription { + case deepLinkInfo(flags: Int32, message: String, entities: [Api.MessageEntity]?) + case deepLinkInfoEmpty public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .chats(let chats): + case .deepLinkInfo(let flags, let message, let entities): if boxed { - buffer.appendInt32(1694474197) + buffer.appendInt32(1783556146) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { + serializeInt32(flags, 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) - } + }} break - case .chatsSlice(let count, let chats): + case .deepLinkInfoEmpty: if boxed { - buffer.appendInt32(-1663561404) - } - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) + buffer.appendInt32(1722786150) } + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .chats(let chats): - return ("chats", [("chats", chats as Any)]) - case .chatsSlice(let count, let chats): - return ("chatsSlice", [("count", count as Any), ("chats", chats as Any)]) + case .deepLinkInfo(let flags, let message, let entities): + return ("deepLinkInfo", [("flags", flags as Any), ("message", message as Any), ("entities", entities as Any)]) + case .deepLinkInfoEmpty: + return ("deepLinkInfoEmpty", []) } } - public static func parse_chats(_ reader: BufferReader) -> Chats? { - var _1: [Api.Chat]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - let _c1 = _1 != nil - if _c1 { - return Api.messages.Chats.chats(chats: _1!) - } - else { - return nil - } - } - public static func parse_chatsSlice(_ reader: BufferReader) -> Chats? { + public static func parse_deepLinkInfo(_ reader: BufferReader) -> DeepLinkInfo? { var _1: Int32? _1 = reader.readInt32() - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } + var _2: String? + _2 = parseString(reader) + var _3: [Api.MessageEntity]? + if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) + } } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.Chats.chatsSlice(count: _1!, chats: _2!) + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.help.DeepLinkInfo.deepLinkInfo(flags: _1!, message: _2!, entities: _3) } else { return nil } } + public static func parse_deepLinkInfoEmpty(_ reader: BufferReader) -> DeepLinkInfo? { + return Api.help.DeepLinkInfo.deepLinkInfoEmpty + } } } -public extension Api.messages { - enum CheckedHistoryImportPeer: TypeConstructorDescription { - case checkedHistoryImportPeer(confirmText: String) +public extension Api.help { + enum InviteText: TypeConstructorDescription { + case inviteText(message: String) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .checkedHistoryImportPeer(let confirmText): + case .inviteText(let message): if boxed { - buffer.appendInt32(-1571952873) + buffer.appendInt32(415997816) } - serializeString(confirmText, buffer: buffer, boxed: false) + serializeString(message, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .checkedHistoryImportPeer(let confirmText): - return ("checkedHistoryImportPeer", [("confirmText", confirmText as Any)]) + case .inviteText(let message): + return ("inviteText", [("message", message as Any)]) } } - public static func parse_checkedHistoryImportPeer(_ reader: BufferReader) -> CheckedHistoryImportPeer? { + public static func parse_inviteText(_ reader: BufferReader) -> InviteText? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil if _c1 { - return Api.messages.CheckedHistoryImportPeer.checkedHistoryImportPeer(confirmText: _1!) + return Api.help.InviteText.inviteText(message: _1!) } else { return nil @@ -930,218 +1048,104 @@ public extension Api.messages { } } -public extension Api.messages { - enum DhConfig: TypeConstructorDescription { - case dhConfig(g: Int32, p: Buffer, version: Int32, random: Buffer) - case dhConfigNotModified(random: Buffer) +public extension Api.help { + enum PassportConfig: TypeConstructorDescription { + case passportConfig(hash: Int32, countriesLangs: Api.DataJSON) + case passportConfigNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .dhConfig(let g, let p, let version, let random): + case .passportConfig(let hash, let countriesLangs): if boxed { - buffer.appendInt32(740433629) + buffer.appendInt32(-1600596305) } - serializeInt32(g, buffer: buffer, boxed: false) - serializeBytes(p, buffer: buffer, boxed: false) - serializeInt32(version, buffer: buffer, boxed: false) - serializeBytes(random, buffer: buffer, boxed: false) + serializeInt32(hash, buffer: buffer, boxed: false) + countriesLangs.serialize(buffer, true) break - case .dhConfigNotModified(let random): + case .passportConfigNotModified: if boxed { - buffer.appendInt32(-1058912715) + buffer.appendInt32(-1078332329) } - serializeBytes(random, buffer: buffer, boxed: false) + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .dhConfig(let g, let p, let version, let random): - return ("dhConfig", [("g", g as Any), ("p", p as Any), ("version", version as Any), ("random", random as Any)]) - case .dhConfigNotModified(let random): - return ("dhConfigNotModified", [("random", random as Any)]) + case .passportConfig(let hash, let countriesLangs): + return ("passportConfig", [("hash", hash as Any), ("countriesLangs", countriesLangs as Any)]) + case .passportConfigNotModified: + return ("passportConfigNotModified", []) } } - public static func parse_dhConfig(_ reader: BufferReader) -> DhConfig? { + public static func parse_passportConfig(_ reader: BufferReader) -> PassportConfig? { var _1: Int32? _1 = reader.readInt32() - var _2: Buffer? - _2 = parseBytes(reader) - var _3: Int32? - _3 = reader.readInt32() - var _4: Buffer? - _4 = parseBytes(reader) + var _2: Api.DataJSON? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.DataJSON + } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.DhConfig.dhConfig(g: _1!, p: _2!, version: _3!, random: _4!) + if _c1 && _c2 { + return Api.help.PassportConfig.passportConfig(hash: _1!, countriesLangs: _2!) } else { return nil } } - public static func parse_dhConfigNotModified(_ reader: BufferReader) -> DhConfig? { - var _1: Buffer? - _1 = parseBytes(reader) - let _c1 = _1 != nil - if _c1 { - return Api.messages.DhConfig.dhConfigNotModified(random: _1!) - } - else { - return nil - } + public static func parse_passportConfigNotModified(_ reader: BufferReader) -> PassportConfig? { + return Api.help.PassportConfig.passportConfigNotModified } } } -public extension Api.messages { - enum Dialogs: TypeConstructorDescription { - case dialogs(dialogs: [Api.Dialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) - case dialogsNotModified(count: Int32) - case dialogsSlice(count: Int32, dialogs: [Api.Dialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) +public extension Api.help { + enum PeerColorOption: TypeConstructorDescription { + case peerColorOption(flags: Int32, colorId: Int32, colors: Api.help.PeerColorSet?, darkColors: Api.help.PeerColorSet?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .dialogs(let dialogs, let messages, let chats, let users): - if boxed { - buffer.appendInt32(364538944) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(dialogs.count)) - for item in dialogs { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - case .dialogsNotModified(let count): - if boxed { - buffer.appendInt32(-253500010) - } - serializeInt32(count, buffer: buffer, boxed: false) - break - case .dialogsSlice(let count, let dialogs, let messages, let chats, let users): + case .peerColorOption(let flags, let colorId, let colors, let darkColors): if boxed { - buffer.appendInt32(1910543603) - } - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(dialogs.count)) - for item in dialogs { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + buffer.appendInt32(324785199) } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(colorId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {colors!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {darkColors!.serialize(buffer, true)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .dialogs(let dialogs, let messages, let chats, let users): - return ("dialogs", [("dialogs", dialogs as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) - case .dialogsNotModified(let count): - return ("dialogsNotModified", [("count", count as Any)]) - case .dialogsSlice(let count, let dialogs, let messages, let chats, let users): - return ("dialogsSlice", [("count", count as Any), ("dialogs", dialogs as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + case .peerColorOption(let flags, let colorId, let colors, let darkColors): + return ("peerColorOption", [("flags", flags as Any), ("colorId", colorId as Any), ("colors", colors as Any), ("darkColors", darkColors as Any)]) } } - public static func parse_dialogs(_ reader: BufferReader) -> Dialogs? { - var _1: [Api.Dialog]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Dialog.self) - } - var _2: [Api.Message]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _3: [Api.Chat]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _4: [Api.User]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.Dialogs.dialogs(dialogs: _1!, messages: _2!, chats: _3!, users: _4!) - } - else { - return nil - } - } - public static func parse_dialogsNotModified(_ reader: BufferReader) -> Dialogs? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.messages.Dialogs.dialogsNotModified(count: _1!) - } - else { - return nil - } - } - public static func parse_dialogsSlice(_ reader: BufferReader) -> Dialogs? { + public static func parse_peerColorOption(_ reader: BufferReader) -> PeerColorOption? { var _1: Int32? _1 = reader.readInt32() - var _2: [Api.Dialog]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Dialog.self) - } - var _3: [Api.Message]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _4: [Api.Chat]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _5: [Api.User]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } + var _2: Int32? + _2 = reader.readInt32() + var _3: Api.help.PeerColorSet? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.help.PeerColorSet + } } + var _4: Api.help.PeerColorSet? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.help.PeerColorSet + } } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.messages.Dialogs.dialogsSlice(count: _1!, dialogs: _2!, messages: _3!, chats: _4!, users: _5!) + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.help.PeerColorOption.peerColorOption(flags: _1!, colorId: _2!, colors: _3, darkColors: _4) } else { return nil @@ -1150,35 +1154,41 @@ public extension Api.messages { } } -public extension Api.messages { - enum DiscussionMessage: TypeConstructorDescription { - case discussionMessage(flags: Int32, messages: [Api.Message], maxId: Int32?, readInboxMaxId: Int32?, readOutboxMaxId: Int32?, unreadCount: Int32, chats: [Api.Chat], users: [Api.User]) +public extension Api.help { + enum PeerColorSet: TypeConstructorDescription { + case peerColorProfileSet(paletteColors: [Int32], bgColors: [Int32], storyColors: [Int32]) + case peerColorSet(colors: [Int32]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .discussionMessage(let flags, let messages, let maxId, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chats, let users): + case .peerColorProfileSet(let paletteColors, let bgColors, let storyColors): if boxed { - buffer.appendInt32(-1506535550) + buffer.appendInt32(1987928555) } - serializeInt32(flags, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) + buffer.appendInt32(Int32(paletteColors.count)) + for item in paletteColors { + serializeInt32(item, buffer: buffer, boxed: false) } - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(maxId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(readInboxMaxId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeInt32(readOutboxMaxId!, buffer: buffer, boxed: false)} - serializeInt32(unreadCount, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) + buffer.appendInt32(Int32(bgColors.count)) + for item in bgColors { + serializeInt32(item, buffer: buffer, boxed: false) } buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + buffer.appendInt32(Int32(storyColors.count)) + for item in storyColors { + serializeInt32(item, buffer: buffer, boxed: false) + } + break + case .peerColorSet(let colors): + if boxed { + buffer.appendInt32(639736408) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(colors.count)) + for item in colors { + serializeInt32(item, buffer: buffer, boxed: false) } break } @@ -1186,44 +1196,44 @@ public extension Api.messages { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .discussionMessage(let flags, let messages, let maxId, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chats, let users): - return ("discussionMessage", [("flags", flags as Any), ("messages", messages as Any), ("maxId", maxId as Any), ("readInboxMaxId", readInboxMaxId as Any), ("readOutboxMaxId", readOutboxMaxId as Any), ("unreadCount", unreadCount as Any), ("chats", chats as Any), ("users", users as Any)]) + case .peerColorProfileSet(let paletteColors, let bgColors, let storyColors): + return ("peerColorProfileSet", [("paletteColors", paletteColors as Any), ("bgColors", bgColors as Any), ("storyColors", storyColors as Any)]) + case .peerColorSet(let colors): + return ("peerColorSet", [("colors", colors as Any)]) } } - public static func parse_discussionMessage(_ reader: BufferReader) -> DiscussionMessage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.Message]? + public static func parse_peerColorProfileSet(_ reader: BufferReader) -> PeerColorSet? { + var _1: [Int32]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _3: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } - var _4: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_4 = reader.readInt32() } - var _5: Int32? - if Int(_1!) & Int(1 << 2) != 0 {_5 = reader.readInt32() } - var _6: Int32? - _6 = reader.readInt32() - var _7: [Api.Chat]? + _1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + var _2: [Int32]? if let _ = reader.readInt32() { - _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + _2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) } - var _8: [Api.User]? + var _3: [Int32]? if let _ = reader.readInt32() { - _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _3 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) } 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 = _6 != nil - let _c7 = _7 != nil - let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.messages.DiscussionMessage.discussionMessage(flags: _1!, messages: _2!, maxId: _3, readInboxMaxId: _4, readOutboxMaxId: _5, unreadCount: _6!, chats: _7!, users: _8!) + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.help.PeerColorSet.peerColorProfileSet(paletteColors: _1!, bgColors: _2!, storyColors: _3!) + } + else { + return nil + } + } + public static func parse_peerColorSet(_ reader: BufferReader) -> PeerColorSet? { + var _1: [Int32]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.help.PeerColorSet.peerColorSet(colors: _1!) } else { return nil @@ -1232,27 +1242,27 @@ public extension Api.messages { } } -public extension Api.messages { - enum EmojiGroups: TypeConstructorDescription { - case emojiGroups(hash: Int32, groups: [Api.EmojiGroup]) - case emojiGroupsNotModified +public extension Api.help { + enum PeerColors: TypeConstructorDescription { + case peerColors(hash: Int32, colors: [Api.help.PeerColorOption]) + case peerColorsNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .emojiGroups(let hash, let groups): + case .peerColors(let hash, let colors): if boxed { - buffer.appendInt32(-2011186869) + buffer.appendInt32(16313608) } serializeInt32(hash, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(groups.count)) - for item in groups { + buffer.appendInt32(Int32(colors.count)) + for item in colors { item.serialize(buffer, true) } break - case .emojiGroupsNotModified: + case .peerColorsNotModified: if boxed { - buffer.appendInt32(1874111879) + buffer.appendInt32(732034510) } break @@ -1261,59 +1271,66 @@ public extension Api.messages { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .emojiGroups(let hash, let groups): - return ("emojiGroups", [("hash", hash as Any), ("groups", groups as Any)]) - case .emojiGroupsNotModified: - return ("emojiGroupsNotModified", []) + case .peerColors(let hash, let colors): + return ("peerColors", [("hash", hash as Any), ("colors", colors as Any)]) + case .peerColorsNotModified: + return ("peerColorsNotModified", []) } } - public static func parse_emojiGroups(_ reader: BufferReader) -> EmojiGroups? { + public static func parse_peerColors(_ reader: BufferReader) -> PeerColors? { var _1: Int32? _1 = reader.readInt32() - var _2: [Api.EmojiGroup]? + var _2: [Api.help.PeerColorOption]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.EmojiGroup.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.help.PeerColorOption.self) } let _c1 = _1 != nil let _c2 = _2 != nil if _c1 && _c2 { - return Api.messages.EmojiGroups.emojiGroups(hash: _1!, groups: _2!) + return Api.help.PeerColors.peerColors(hash: _1!, colors: _2!) } else { return nil } } - public static func parse_emojiGroupsNotModified(_ reader: BufferReader) -> EmojiGroups? { - return Api.messages.EmojiGroups.emojiGroupsNotModified + public static func parse_peerColorsNotModified(_ reader: BufferReader) -> PeerColors? { + return Api.help.PeerColors.peerColorsNotModified } } } -public extension Api.messages { - enum ExportedChatInvite: TypeConstructorDescription { - case exportedChatInvite(invite: Api.ExportedChatInvite, users: [Api.User]) - case exportedChatInviteReplaced(invite: Api.ExportedChatInvite, newInvite: Api.ExportedChatInvite, users: [Api.User]) +public extension Api.help { + enum PremiumPromo: TypeConstructorDescription { + case premiumPromo(statusText: String, statusEntities: [Api.MessageEntity], videoSections: [String], videos: [Api.Document], periodOptions: [Api.PremiumSubscriptionOption], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .exportedChatInvite(let invite, let users): + case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let periodOptions, let users): if boxed { - buffer.appendInt32(410107472) + buffer.appendInt32(1395946908) } - invite.serialize(buffer, true) + serializeString(statusText, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { + buffer.appendInt32(Int32(statusEntities.count)) + for item in statusEntities { item.serialize(buffer, true) } - break - case .exportedChatInviteReplaced(let invite, let newInvite, let users): - if boxed { - buffer.appendInt32(572915951) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(videoSections.count)) + for item in videoSections { + serializeString(item, buffer: buffer, boxed: false) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(videos.count)) + for item in videos { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(periodOptions.count)) + for item in periodOptions { + item.serialize(buffer, true) } - invite.serialize(buffer, true) - newInvite.serialize(buffer, true) buffer.appendInt32(481674261) buffer.appendInt32(Int32(users.count)) for item in users { @@ -1325,49 +1342,42 @@ public extension Api.messages { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .exportedChatInvite(let invite, let users): - return ("exportedChatInvite", [("invite", invite as Any), ("users", users as Any)]) - case .exportedChatInviteReplaced(let invite, let newInvite, let users): - return ("exportedChatInviteReplaced", [("invite", invite as Any), ("newInvite", newInvite as Any), ("users", users as Any)]) + case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let periodOptions, let users): + return ("premiumPromo", [("statusText", statusText as Any), ("statusEntities", statusEntities as Any), ("videoSections", videoSections as Any), ("videos", videos as Any), ("periodOptions", periodOptions as Any), ("users", users as Any)]) } } - public static func parse_exportedChatInvite(_ reader: BufferReader) -> ExportedChatInvite? { - var _1: Api.ExportedChatInvite? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite - } - var _2: [Api.User]? + public static func parse_premiumPromo(_ reader: BufferReader) -> PremiumPromo? { + var _1: String? + _1 = parseString(reader) + var _2: [Api.MessageEntity]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.ExportedChatInvite.exportedChatInvite(invite: _1!, users: _2!) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) } - else { - return nil + var _3: [String]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self) } - } - public static func parse_exportedChatInviteReplaced(_ reader: BufferReader) -> ExportedChatInvite? { - var _1: Api.ExportedChatInvite? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite + var _4: [Api.Document]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) } - var _2: Api.ExportedChatInvite? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite + var _5: [Api.PremiumSubscriptionOption]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PremiumSubscriptionOption.self) } - var _3: [Api.User]? + var _6: [Api.User]? if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.ExportedChatInvite.exportedChatInviteReplaced(invite: _1!, newInvite: _2!, users: _3!) + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.help.PremiumPromo.premiumPromo(statusText: _1!, statusEntities: _2!, videoSections: _3!, videos: _4!, periodOptions: _5!, users: _6!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api27.swift b/submodules/TelegramApi/Sources/Api27.swift index fde364d4bb6..90373cb39cb 100644 --- a/submodules/TelegramApi/Sources/Api27.swift +++ b/submodules/TelegramApi/Sources/Api27.swift @@ -1,17 +1,20 @@ -public extension Api.messages { - enum ExportedChatInvites: TypeConstructorDescription { - case exportedChatInvites(count: Int32, invites: [Api.ExportedChatInvite], users: [Api.User]) +public extension Api.help { + enum PromoData: TypeConstructorDescription { + case promoData(flags: Int32, expires: Int32, peer: Api.Peer, chats: [Api.Chat], users: [Api.User], psaType: String?, psaMessage: String?) + case promoDataEmpty(expires: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .exportedChatInvites(let count, let invites, let users): + case .promoData(let flags, let expires, let peer, let chats, let users, let psaType, let psaMessage): if boxed { - buffer.appendInt32(-1111085620) + buffer.appendInt32(-1942390465) } - serializeInt32(count, buffer: buffer, boxed: false) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(expires, buffer: buffer, boxed: false) + peer.serialize(buffer, true) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(invites.count)) - for item in invites { + buffer.appendInt32(Int32(chats.count)) + for item in chats { item.serialize(buffer, true) } buffer.appendInt32(481674261) @@ -19,33 +22,68 @@ public extension Api.messages { for item in users { item.serialize(buffer, true) } + if Int(flags) & Int(1 << 1) != 0 {serializeString(psaType!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(psaMessage!, buffer: buffer, boxed: false)} + break + case .promoDataEmpty(let expires): + if boxed { + buffer.appendInt32(-1728664459) + } + serializeInt32(expires, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .exportedChatInvites(let count, let invites, let users): - return ("exportedChatInvites", [("count", count as Any), ("invites", invites as Any), ("users", users as Any)]) + case .promoData(let flags, let expires, let peer, let chats, let users, let psaType, let psaMessage): + return ("promoData", [("flags", flags as Any), ("expires", expires as Any), ("peer", peer as Any), ("chats", chats as Any), ("users", users as Any), ("psaType", psaType as Any), ("psaMessage", psaMessage as Any)]) + case .promoDataEmpty(let expires): + return ("promoDataEmpty", [("expires", expires as Any)]) } } - public static func parse_exportedChatInvites(_ reader: BufferReader) -> ExportedChatInvites? { + public static func parse_promoData(_ reader: BufferReader) -> PromoData? { var _1: Int32? _1 = reader.readInt32() - var _2: [Api.ExportedChatInvite]? + var _2: Int32? + _2 = reader.readInt32() + var _3: Api.Peer? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _4: [Api.Chat]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ExportedChatInvite.self) + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) } - var _3: [Api.User]? + var _5: [Api.User]? if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } + var _6: String? + if Int(_1!) & Int(1 << 1) != 0 {_6 = parseString(reader) } + var _7: String? + if Int(_1!) & Int(1 << 2) != 0 {_7 = parseString(reader) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.ExportedChatInvites.exportedChatInvites(count: _1!, invites: _2!, users: _3!) + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.help.PromoData.promoData(flags: _1!, expires: _2!, peer: _3!, chats: _4!, users: _5!, psaType: _6, psaMessage: _7) + } + else { + return nil + } + } + public static func parse_promoDataEmpty(_ reader: BufferReader) -> PromoData? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.help.PromoData.promoDataEmpty(expires: _1!) } else { return nil @@ -54,150 +92,138 @@ public extension Api.messages { } } -public extension Api.messages { - enum FavedStickers: TypeConstructorDescription { - case favedStickers(hash: Int64, packs: [Api.StickerPack], stickers: [Api.Document]) - case favedStickersNotModified +public extension Api.help { + enum RecentMeUrls: TypeConstructorDescription { + case recentMeUrls(urls: [Api.RecentMeUrl], chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .favedStickers(let hash, let packs, let stickers): + case .recentMeUrls(let urls, let chats, let users): if boxed { - buffer.appendInt32(750063767) + buffer.appendInt32(235081943) } - serializeInt64(hash, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(packs.count)) - for item in packs { + buffer.appendInt32(Int32(urls.count)) + for item in urls { item.serialize(buffer, true) } buffer.appendInt32(481674261) - buffer.appendInt32(Int32(stickers.count)) - for item in stickers { + buffer.appendInt32(Int32(chats.count)) + for item in chats { item.serialize(buffer, true) } - break - case .favedStickersNotModified: - if boxed { - buffer.appendInt32(-1634752813) + 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 .favedStickers(let hash, let packs, let stickers): - return ("favedStickers", [("hash", hash as Any), ("packs", packs as Any), ("stickers", stickers as Any)]) - case .favedStickersNotModified: - return ("favedStickersNotModified", []) + case .recentMeUrls(let urls, let chats, let users): + return ("recentMeUrls", [("urls", urls as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_favedStickers(_ reader: BufferReader) -> FavedStickers? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.StickerPack]? + public static func parse_recentMeUrls(_ reader: BufferReader) -> RecentMeUrls? { + var _1: [Api.RecentMeUrl]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RecentMeUrl.self) + } + var _2: [Api.Chat]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) } - var _3: [Api.Document]? + var _3: [Api.User]? if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil if _c1 && _c2 && _c3 { - return Api.messages.FavedStickers.favedStickers(hash: _1!, packs: _2!, stickers: _3!) + return Api.help.RecentMeUrls.recentMeUrls(urls: _1!, chats: _2!, users: _3!) } else { return nil } } - public static func parse_favedStickersNotModified(_ reader: BufferReader) -> FavedStickers? { - return Api.messages.FavedStickers.favedStickersNotModified - } } } -public extension Api.messages { - enum FeaturedStickers: TypeConstructorDescription { - case featuredStickers(flags: Int32, hash: Int64, count: Int32, sets: [Api.StickerSetCovered], unread: [Int64]) - case featuredStickersNotModified(count: Int32) +public extension Api.help { + enum Support: TypeConstructorDescription { + case support(phoneNumber: String, user: Api.User) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .featuredStickers(let flags, let hash, let count, let sets, let unread): - if boxed { - buffer.appendInt32(-1103615738) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(sets.count)) - for item in sets { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(unread.count)) - for item in unread { - serializeInt64(item, buffer: buffer, boxed: false) - } - break - case .featuredStickersNotModified(let count): + case .support(let phoneNumber, let user): if boxed { - buffer.appendInt32(-958657434) + buffer.appendInt32(398898678) } - serializeInt32(count, buffer: buffer, boxed: false) + serializeString(phoneNumber, buffer: buffer, boxed: false) + user.serialize(buffer, true) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .featuredStickers(let flags, let hash, let count, let sets, let unread): - return ("featuredStickers", [("flags", flags as Any), ("hash", hash as Any), ("count", count as Any), ("sets", sets as Any), ("unread", unread as Any)]) - case .featuredStickersNotModified(let count): - return ("featuredStickersNotModified", [("count", count as Any)]) + case .support(let phoneNumber, let user): + return ("support", [("phoneNumber", phoneNumber as Any), ("user", user as Any)]) } } - public static func parse_featuredStickers(_ reader: BufferReader) -> FeaturedStickers? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int32? - _3 = reader.readInt32() - var _4: [Api.StickerSetCovered]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self) - } - var _5: [Int64]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + public static func parse_support(_ reader: BufferReader) -> Support? { + var _1: String? + _1 = parseString(reader) + var _2: Api.User? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.User } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.messages.FeaturedStickers.featuredStickers(flags: _1!, hash: _2!, count: _3!, sets: _4!, unread: _5!) + if _c1 && _c2 { + return Api.help.Support.support(phoneNumber: _1!, user: _2!) } else { return nil } } - public static func parse_featuredStickersNotModified(_ reader: BufferReader) -> FeaturedStickers? { - var _1: Int32? - _1 = reader.readInt32() + + } +} +public extension Api.help { + enum SupportName: TypeConstructorDescription { + case supportName(name: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .supportName(let name): + if boxed { + buffer.appendInt32(-1945767479) + } + serializeString(name, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .supportName(let name): + return ("supportName", [("name", name as Any)]) + } + } + + public static func parse_supportName(_ reader: BufferReader) -> SupportName? { + var _1: String? + _1 = parseString(reader) let _c1 = _1 != nil if _c1 { - return Api.messages.FeaturedStickers.featuredStickersNotModified(count: _1!) + return Api.help.SupportName.supportName(name: _1!) } else { return nil @@ -206,82 +232,58 @@ public extension Api.messages { } } -public extension Api.messages { - enum ForumTopics: TypeConstructorDescription { - case forumTopics(flags: Int32, count: Int32, topics: [Api.ForumTopic], messages: [Api.Message], chats: [Api.Chat], users: [Api.User], pts: Int32) +public extension Api.help { + enum TermsOfService: TypeConstructorDescription { + case termsOfService(flags: Int32, id: Api.DataJSON, text: String, entities: [Api.MessageEntity], minAgeConfirm: Int32?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .forumTopics(let flags, let count, let topics, let messages, let chats, let users, let pts): + case .termsOfService(let flags, let id, let text, let entities, let minAgeConfirm): if boxed { - buffer.appendInt32(913709011) + buffer.appendInt32(2013922064) } serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(topics.count)) - for item in topics { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } + id.serialize(buffer, true) + serializeString(text, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { + buffer.appendInt32(Int32(entities.count)) + for item in entities { item.serialize(buffer, true) } - serializeInt32(pts, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(minAgeConfirm!, buffer: buffer, boxed: false)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .forumTopics(let flags, let count, let topics, let messages, let chats, let users, let pts): - return ("forumTopics", [("flags", flags as Any), ("count", count as Any), ("topics", topics as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any), ("pts", pts as Any)]) + case .termsOfService(let flags, let id, let text, let entities, let minAgeConfirm): + return ("termsOfService", [("flags", flags as Any), ("id", id as Any), ("text", text as Any), ("entities", entities as Any), ("minAgeConfirm", minAgeConfirm as Any)]) } } - public static func parse_forumTopics(_ reader: BufferReader) -> ForumTopics? { + public static func parse_termsOfService(_ reader: BufferReader) -> TermsOfService? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: [Api.ForumTopic]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ForumTopic.self) - } - var _4: [Api.Message]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _5: [Api.Chat]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + var _2: Api.DataJSON? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.DataJSON } - var _6: [Api.User]? + var _3: String? + _3 = parseString(reader) + var _4: [Api.MessageEntity]? if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) } - var _7: Int32? - _7 = reader.readInt32() + var _5: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_5 = 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.messages.ForumTopics.forumTopics(flags: _1!, count: _2!, topics: _3!, messages: _4!, chats: _5!, users: _6!, pts: _7!) + let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.help.TermsOfService.termsOfService(flags: _1!, id: _2!, text: _3!, entities: _4!, minAgeConfirm: _5) } else { return nil @@ -290,144 +292,180 @@ public extension Api.messages { } } -public extension Api.messages { - enum FoundStickerSets: TypeConstructorDescription { - case foundStickerSets(hash: Int64, sets: [Api.StickerSetCovered]) - case foundStickerSetsNotModified +public extension Api.help { + enum TermsOfServiceUpdate: TypeConstructorDescription { + case termsOfServiceUpdate(expires: Int32, termsOfService: Api.help.TermsOfService) + case termsOfServiceUpdateEmpty(expires: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .foundStickerSets(let hash, let sets): + case .termsOfServiceUpdate(let expires, let termsOfService): if boxed { - buffer.appendInt32(-1963942446) - } - serializeInt64(hash, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(sets.count)) - for item in sets { - item.serialize(buffer, true) + buffer.appendInt32(686618977) } + serializeInt32(expires, buffer: buffer, boxed: false) + termsOfService.serialize(buffer, true) break - case .foundStickerSetsNotModified: + case .termsOfServiceUpdateEmpty(let expires): if boxed { - buffer.appendInt32(223655517) + buffer.appendInt32(-483352705) } - + serializeInt32(expires, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .foundStickerSets(let hash, let sets): - return ("foundStickerSets", [("hash", hash as Any), ("sets", sets as Any)]) - case .foundStickerSetsNotModified: - return ("foundStickerSetsNotModified", []) + case .termsOfServiceUpdate(let expires, let termsOfService): + return ("termsOfServiceUpdate", [("expires", expires as Any), ("termsOfService", termsOfService as Any)]) + case .termsOfServiceUpdateEmpty(let expires): + return ("termsOfServiceUpdateEmpty", [("expires", expires as Any)]) } } - public static func parse_foundStickerSets(_ reader: BufferReader) -> FoundStickerSets? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.StickerSetCovered]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self) + public static func parse_termsOfServiceUpdate(_ reader: BufferReader) -> TermsOfServiceUpdate? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.help.TermsOfService? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.help.TermsOfService } let _c1 = _1 != nil let _c2 = _2 != nil if _c1 && _c2 { - return Api.messages.FoundStickerSets.foundStickerSets(hash: _1!, sets: _2!) + return Api.help.TermsOfServiceUpdate.termsOfServiceUpdate(expires: _1!, termsOfService: _2!) } else { return nil } } - public static func parse_foundStickerSetsNotModified(_ reader: BufferReader) -> FoundStickerSets? { - return Api.messages.FoundStickerSets.foundStickerSetsNotModified + public static func parse_termsOfServiceUpdateEmpty(_ reader: BufferReader) -> TermsOfServiceUpdate? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.help.TermsOfServiceUpdate.termsOfServiceUpdateEmpty(expires: _1!) + } + else { + return nil + } } } } -public extension Api.messages { - enum HighScores: TypeConstructorDescription { - case highScores(scores: [Api.HighScore], users: [Api.User]) +public extension Api.help { + enum UserInfo: TypeConstructorDescription { + case userInfo(message: String, entities: [Api.MessageEntity], author: String, date: Int32) + case userInfoEmpty public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .highScores(let scores, let users): + case .userInfo(let message, let entities, let author, let date): if boxed { - buffer.appendInt32(-1707344487) + buffer.appendInt32(32192344) } + serializeString(message, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(scores.count)) - for item in scores { + buffer.appendInt32(Int32(entities.count)) + for item in entities { item.serialize(buffer, true) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + serializeString(author, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + break + case .userInfoEmpty: + if boxed { + buffer.appendInt32(-206688531) } + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .highScores(let scores, let users): - return ("highScores", [("scores", scores as Any), ("users", users as Any)]) + case .userInfo(let message, let entities, let author, let date): + return ("userInfo", [("message", message as Any), ("entities", entities as Any), ("author", author as Any), ("date", date as Any)]) + case .userInfoEmpty: + return ("userInfoEmpty", []) } } - public static func parse_highScores(_ reader: BufferReader) -> HighScores? { - var _1: [Api.HighScore]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.HighScore.self) - } - var _2: [Api.User]? + public static func parse_userInfo(_ reader: BufferReader) -> UserInfo? { + var _1: String? + _1 = parseString(reader) + var _2: [Api.MessageEntity]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) } + var _3: String? + _3 = parseString(reader) + var _4: Int32? + _4 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.HighScores.highScores(scores: _1!, users: _2!) + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.help.UserInfo.userInfo(message: _1!, entities: _2!, author: _3!, date: _4!) } else { return nil } } + public static func parse_userInfoEmpty(_ reader: BufferReader) -> UserInfo? { + return Api.help.UserInfo.userInfoEmpty + } } } public extension Api.messages { - enum HistoryImport: TypeConstructorDescription { - case historyImport(id: Int64) + enum AffectedFoundMessages: TypeConstructorDescription { + case affectedFoundMessages(pts: Int32, ptsCount: Int32, offset: Int32, messages: [Int32]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .historyImport(let id): + case .affectedFoundMessages(let pts, let ptsCount, let offset, let messages): if boxed { - buffer.appendInt32(375566091) + buffer.appendInt32(-275956116) + } + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + serializeInt32(offset, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + serializeInt32(item, buffer: buffer, boxed: false) } - serializeInt64(id, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .historyImport(let id): - return ("historyImport", [("id", id as Any)]) + case .affectedFoundMessages(let pts, let ptsCount, let offset, let messages): + return ("affectedFoundMessages", [("pts", pts as Any), ("ptsCount", ptsCount as Any), ("offset", offset as Any), ("messages", messages as Any)]) } } - public static func parse_historyImport(_ reader: BufferReader) -> HistoryImport? { - var _1: Int64? - _1 = reader.readInt64() + public static func parse_affectedFoundMessages(_ reader: BufferReader) -> AffectedFoundMessages? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: [Int32]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } let _c1 = _1 != nil - if _c1 { - return Api.messages.HistoryImport.historyImport(id: _1!) + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.messages.AffectedFoundMessages.affectedFoundMessages(pts: _1!, ptsCount: _2!, offset: _3!, messages: _4!) } else { return nil @@ -437,37 +475,41 @@ public extension Api.messages { } } public extension Api.messages { - enum HistoryImportParsed: TypeConstructorDescription { - case historyImportParsed(flags: Int32, title: String?) + enum AffectedHistory: TypeConstructorDescription { + case affectedHistory(pts: Int32, ptsCount: Int32, offset: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .historyImportParsed(let flags, let title): + case .affectedHistory(let pts, let ptsCount, let offset): if boxed { - buffer.appendInt32(1578088377) + buffer.appendInt32(-1269012015) } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + serializeInt32(offset, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .historyImportParsed(let flags, let title): - return ("historyImportParsed", [("flags", flags as Any), ("title", title as Any)]) + case .affectedHistory(let pts, let ptsCount, let offset): + return ("affectedHistory", [("pts", pts as Any), ("ptsCount", ptsCount as Any), ("offset", offset as Any)]) } } - public static func parse_historyImportParsed(_ reader: BufferReader) -> HistoryImportParsed? { + public static func parse_affectedHistory(_ reader: BufferReader) -> AffectedHistory? { var _1: Int32? _1 = reader.readInt32() - var _2: String? - if Int(_1!) & Int(1 << 2) != 0 {_2 = parseString(reader) } + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 2) == 0) || _2 != nil - if _c1 && _c2 { - return Api.messages.HistoryImportParsed.historyImportParsed(flags: _1!, title: _2) + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.messages.AffectedHistory.affectedHistory(pts: _1!, ptsCount: _2!, offset: _3!) } else { return nil @@ -477,59 +519,37 @@ public extension Api.messages { } } public extension Api.messages { - enum InactiveChats: TypeConstructorDescription { - case inactiveChats(dates: [Int32], chats: [Api.Chat], users: [Api.User]) + enum AffectedMessages: TypeConstructorDescription { + case affectedMessages(pts: Int32, ptsCount: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .inactiveChats(let dates, let chats, let users): + case .affectedMessages(let pts, let ptsCount): if boxed { - buffer.appendInt32(-1456996667) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(dates.count)) - for item in dates { - serializeInt32(item, buffer: buffer, boxed: false) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + buffer.appendInt32(-2066640507) } + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .inactiveChats(let dates, let chats, let users): - return ("inactiveChats", [("dates", dates as Any), ("chats", chats as Any), ("users", users as Any)]) + case .affectedMessages(let pts, let ptsCount): + return ("affectedMessages", [("pts", pts as Any), ("ptsCount", ptsCount as Any)]) } } - public static func parse_inactiveChats(_ reader: BufferReader) -> InactiveChats? { - var _1: [Int32]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } + public static func parse_affectedMessages(_ reader: BufferReader) -> AffectedMessages? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.InactiveChats.inactiveChats(dates: _1!, chats: _2!, users: _3!) + if _c1 && _c2 { + return Api.messages.AffectedMessages.affectedMessages(pts: _1!, ptsCount: _2!) } else { return nil @@ -539,107 +559,101 @@ public extension Api.messages { } } public extension Api.messages { - enum MessageEditData: TypeConstructorDescription { - case messageEditData(flags: Int32) + enum AllStickers: TypeConstructorDescription { + case allStickers(hash: Int64, sets: [Api.StickerSet]) + case allStickersNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .messageEditData(let flags): + case .allStickers(let hash, let sets): if boxed { - buffer.appendInt32(649453030) + buffer.appendInt32(-843329861) + } + serializeInt64(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(sets.count)) + for item in sets { + item.serialize(buffer, true) } - serializeInt32(flags, buffer: buffer, boxed: false) break - } - } - + case .allStickersNotModified: + if boxed { + buffer.appendInt32(-395967805) + } + + break + } + } + public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .messageEditData(let flags): - return ("messageEditData", [("flags", flags as Any)]) + case .allStickers(let hash, let sets): + return ("allStickers", [("hash", hash as Any), ("sets", sets as Any)]) + case .allStickersNotModified: + return ("allStickersNotModified", []) } } - public static func parse_messageEditData(_ reader: BufferReader) -> MessageEditData? { - var _1: Int32? - _1 = reader.readInt32() + public static func parse_allStickers(_ reader: BufferReader) -> AllStickers? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.StickerSet]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSet.self) + } let _c1 = _1 != nil - if _c1 { - return Api.messages.MessageEditData.messageEditData(flags: _1!) + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.messages.AllStickers.allStickers(hash: _1!, sets: _2!) } else { return nil } } + public static func parse_allStickersNotModified(_ reader: BufferReader) -> AllStickers? { + return Api.messages.AllStickers.allStickersNotModified + } } } public extension Api.messages { - enum MessageReactionsList: TypeConstructorDescription { - case messageReactionsList(flags: Int32, count: Int32, reactions: [Api.MessagePeerReaction], chats: [Api.Chat], users: [Api.User], nextOffset: String?) + enum ArchivedStickers: TypeConstructorDescription { + case archivedStickers(count: Int32, sets: [Api.StickerSetCovered]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .messageReactionsList(let flags, let count, let reactions, let chats, let users, let nextOffset): + case .archivedStickers(let count, let sets): if boxed { - buffer.appendInt32(834488621) + buffer.appendInt32(1338747336) } - serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(count, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(reactions.count)) - for item in reactions { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { + buffer.appendInt32(Int32(sets.count)) + for item in sets { item.serialize(buffer, true) } - if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .messageReactionsList(let flags, let count, let reactions, let chats, let users, let nextOffset): - return ("messageReactionsList", [("flags", flags as Any), ("count", count as Any), ("reactions", reactions as Any), ("chats", chats as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)]) + case .archivedStickers(let count, let sets): + return ("archivedStickers", [("count", count as Any), ("sets", sets as Any)]) } } - public static func parse_messageReactionsList(_ reader: BufferReader) -> MessageReactionsList? { + public static func parse_archivedStickers(_ reader: BufferReader) -> ArchivedStickers? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: [Api.MessagePeerReaction]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessagePeerReaction.self) - } - var _4: [Api.Chat]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _5: [Api.User]? + var _2: [Api.StickerSetCovered]? if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self) } - var _6: String? - if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.messages.MessageReactionsList.messageReactionsList(flags: _1!, count: _2!, reactions: _3!, chats: _4!, users: _5!, nextOffset: _6) + if _c1 && _c2 { + return Api.messages.ArchivedStickers.archivedStickers(count: _1!, sets: _2!) } else { return nil @@ -649,149 +663,174 @@ public extension Api.messages { } } public extension Api.messages { - enum MessageViews: TypeConstructorDescription { - case messageViews(views: [Api.MessageViews], chats: [Api.Chat], users: [Api.User]) + enum AvailableReactions: TypeConstructorDescription { + case availableReactions(hash: Int32, reactions: [Api.AvailableReaction]) + case availableReactionsNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .messageViews(let views, let chats, let users): + case .availableReactions(let hash, let reactions): if boxed { - buffer.appendInt32(-1228606141) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(views.count)) - for item in views { - item.serialize(buffer, true) + buffer.appendInt32(1989032621) } + serializeInt32(hash, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { + buffer.appendInt32(Int32(reactions.count)) + for item in reactions { item.serialize(buffer, true) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + break + case .availableReactionsNotModified: + if boxed { + buffer.appendInt32(-1626924713) } + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .messageViews(let views, let chats, let users): - return ("messageViews", [("views", views as Any), ("chats", chats as Any), ("users", users as Any)]) + case .availableReactions(let hash, let reactions): + return ("availableReactions", [("hash", hash as Any), ("reactions", reactions as Any)]) + case .availableReactionsNotModified: + return ("availableReactionsNotModified", []) } } - public static func parse_messageViews(_ reader: BufferReader) -> MessageViews? { - var _1: [Api.MessageViews]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageViews.self) - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? + public static func parse_availableReactions(_ reader: BufferReader) -> AvailableReactions? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.AvailableReaction]? if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.AvailableReaction.self) } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.MessageViews.messageViews(views: _1!, chats: _2!, users: _3!) + if _c1 && _c2 { + return Api.messages.AvailableReactions.availableReactions(hash: _1!, reactions: _2!) } else { return nil } } + public static func parse_availableReactionsNotModified(_ reader: BufferReader) -> AvailableReactions? { + return Api.messages.AvailableReactions.availableReactionsNotModified + } } } public extension Api.messages { - enum Messages: TypeConstructorDescription { - case channelMessages(flags: Int32, pts: Int32, count: Int32, offsetIdOffset: Int32?, messages: [Api.Message], topics: [Api.ForumTopic], chats: [Api.Chat], users: [Api.User]) - case messages(messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) - case messagesNotModified(count: Int32) - case messagesSlice(flags: Int32, count: Int32, nextRate: Int32?, offsetIdOffset: Int32?, messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + enum BotApp: TypeConstructorDescription { + case botApp(flags: Int32, app: Api.BotApp) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .channelMessages(let flags, let pts, let count, let offsetIdOffset, let messages, let topics, let chats, let users): + case .botApp(let flags, let app): if boxed { - buffer.appendInt32(-948520370) + buffer.appendInt32(-347034123) } serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(count, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {serializeInt32(offsetIdOffset!, buffer: buffer, boxed: false)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(topics.count)) - for item in topics { - 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) - } + app.serialize(buffer, true) break - case .messages(let messages, let chats, let users): - if boxed { - buffer.appendInt32(-1938715001) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - case .messagesNotModified(let count): + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .botApp(let flags, let app): + return ("botApp", [("flags", flags as Any), ("app", app as Any)]) + } + } + + public static func parse_botApp(_ reader: BufferReader) -> BotApp? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.BotApp? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.BotApp + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.messages.BotApp.botApp(flags: _1!, app: _2!) + } + else { + return nil + } + } + + } +} +public extension Api.messages { + enum BotCallbackAnswer: TypeConstructorDescription { + case botCallbackAnswer(flags: Int32, message: String?, url: String?, cacheTime: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .botCallbackAnswer(let flags, let message, let url, let cacheTime): if boxed { - buffer.appendInt32(1951620897) + buffer.appendInt32(911761060) } - serializeInt32(count, buffer: buffer, boxed: false) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(message!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(url!, buffer: buffer, boxed: false)} + serializeInt32(cacheTime, buffer: buffer, boxed: false) break - case .messagesSlice(let flags, let count, let nextRate, let offsetIdOffset, let messages, let chats, let users): + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .botCallbackAnswer(let flags, let message, let url, let cacheTime): + return ("botCallbackAnswer", [("flags", flags as Any), ("message", message as Any), ("url", url as Any), ("cacheTime", cacheTime as Any)]) + } + } + + public static func parse_botCallbackAnswer(_ reader: BufferReader) -> BotCallbackAnswer? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + if Int(_1!) & Int(1 << 0) != 0 {_2 = parseString(reader) } + var _3: String? + if Int(_1!) & Int(1 << 2) != 0 {_3 = parseString(reader) } + var _4: Int32? + _4 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.messages.BotCallbackAnswer.botCallbackAnswer(flags: _1!, message: _2, url: _3, cacheTime: _4!) + } + else { + return nil + } + } + + } +} +public extension Api.messages { + enum BotResults: TypeConstructorDescription { + case botResults(flags: Int32, queryId: Int64, nextOffset: String?, switchPm: Api.InlineBotSwitchPM?, switchWebview: Api.InlineBotWebView?, results: [Api.BotInlineResult], cacheTime: Int32, users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .botResults(let flags, let queryId, let nextOffset, let switchPm, let switchWebview, let results, let cacheTime, let users): if boxed { - buffer.appendInt32(978610270) + buffer.appendInt32(-534646026) } serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(count, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(nextRate!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeInt32(offsetIdOffset!, buffer: buffer, boxed: false)} + serializeInt64(queryId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {switchPm!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {switchWebview!.serialize(buffer, true)} buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { + buffer.appendInt32(Int32(results.count)) + for item in results { item.serialize(buffer, true) } + serializeInt32(cacheTime, buffer: buffer, boxed: false) buffer.appendInt32(481674261) buffer.appendInt32(Int32(users.count)) for item in users { @@ -803,121 +842,46 @@ public extension Api.messages { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .channelMessages(let flags, let pts, let count, let offsetIdOffset, let messages, let topics, let chats, let users): - return ("channelMessages", [("flags", flags as Any), ("pts", pts as Any), ("count", count as Any), ("offsetIdOffset", offsetIdOffset as Any), ("messages", messages as Any), ("topics", topics as Any), ("chats", chats as Any), ("users", users as Any)]) - case .messages(let messages, let chats, let users): - return ("messages", [("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) - case .messagesNotModified(let count): - return ("messagesNotModified", [("count", count as Any)]) - case .messagesSlice(let flags, let count, let nextRate, let offsetIdOffset, let messages, let chats, let users): - return ("messagesSlice", [("flags", flags as Any), ("count", count as Any), ("nextRate", nextRate as Any), ("offsetIdOffset", offsetIdOffset as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + case .botResults(let flags, let queryId, let nextOffset, let switchPm, let switchWebview, let results, let cacheTime, let users): + return ("botResults", [("flags", flags as Any), ("queryId", queryId as Any), ("nextOffset", nextOffset as Any), ("switchPm", switchPm as Any), ("switchWebview", switchWebview as Any), ("results", results as Any), ("cacheTime", cacheTime as Any), ("users", users as Any)]) } } - public static func parse_channelMessages(_ reader: BufferReader) -> Messages? { + public static func parse_botResults(_ reader: BufferReader) -> BotResults? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - if Int(_1!) & Int(1 << 2) != 0 {_4 = reader.readInt32() } - var _5: [Api.Message]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _6: [Api.ForumTopic]? - if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ForumTopic.self) - } - var _7: [Api.Chat]? + var _2: Int64? + _2 = reader.readInt64() + var _3: String? + if Int(_1!) & Int(1 << 1) != 0 {_3 = parseString(reader) } + var _4: Api.InlineBotSwitchPM? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.InlineBotSwitchPM + } } + var _5: Api.InlineBotWebView? + if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.InlineBotWebView + } } + var _6: [Api.BotInlineResult]? if let _ = reader.readInt32() { - _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.BotInlineResult.self) } + var _7: Int32? + _7 = reader.readInt32() var _8: [Api.User]? if let _ = reader.readInt32() { _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - let _c5 = _5 != nil + let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _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.messages.Messages.channelMessages(flags: _1!, pts: _2!, count: _3!, offsetIdOffset: _4, messages: _5!, topics: _6!, chats: _7!, users: _8!) - } - else { - return nil - } - } - public static func parse_messages(_ reader: BufferReader) -> Messages? { - var _1: [Api.Message]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.Messages.messages(messages: _1!, chats: _2!, users: _3!) - } - else { - return nil - } - } - public static func parse_messagesNotModified(_ reader: BufferReader) -> Messages? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.messages.Messages.messagesNotModified(count: _1!) - } - else { - return nil - } - } - public static func parse_messagesSlice(_ reader: BufferReader) -> Messages? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } - var _4: Int32? - if Int(_1!) & Int(1 << 2) != 0 {_4 = reader.readInt32() } - var _5: [Api.Message]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _6: [Api.Chat]? - if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _7: [Api.User]? - if let _ = reader.readInt32() { - _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.messages.Messages.messagesSlice(flags: _1!, count: _2!, nextRate: _3, offsetIdOffset: _4, messages: _5!, chats: _6!, users: _7!) + return Api.messages.BotResults.botResults(flags: _1!, queryId: _2!, nextOffset: _3, switchPm: _4, switchWebview: _5, results: _6!, cacheTime: _7!, users: _8!) } else { return nil @@ -927,28 +891,18 @@ public extension Api.messages { } } public extension Api.messages { - enum PeerDialogs: TypeConstructorDescription { - case peerDialogs(dialogs: [Api.Dialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User], state: Api.updates.State) + enum ChatAdminsWithInvites: TypeConstructorDescription { + case chatAdminsWithInvites(admins: [Api.ChatAdminWithInvites], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .peerDialogs(let dialogs, let messages, let chats, let users, let state): + case .chatAdminsWithInvites(let admins, let users): if boxed { - buffer.appendInt32(863093588) + buffer.appendInt32(-1231326505) } buffer.appendInt32(481674261) - buffer.appendInt32(Int32(dialogs.count)) - for item in dialogs { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { + buffer.appendInt32(Int32(admins.count)) + for item in admins { item.serialize(buffer, true) } buffer.appendInt32(481674261) @@ -956,46 +910,30 @@ public extension Api.messages { for item in users { item.serialize(buffer, true) } - state.serialize(buffer, true) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .peerDialogs(let dialogs, let messages, let chats, let users, let state): - return ("peerDialogs", [("dialogs", dialogs as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any), ("state", state as Any)]) + case .chatAdminsWithInvites(let admins, let users): + return ("chatAdminsWithInvites", [("admins", admins as Any), ("users", users as Any)]) } } - public static func parse_peerDialogs(_ reader: BufferReader) -> PeerDialogs? { - var _1: [Api.Dialog]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Dialog.self) - } - var _2: [Api.Message]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _3: [Api.Chat]? + public static func parse_chatAdminsWithInvites(_ reader: BufferReader) -> ChatAdminsWithInvites? { + var _1: [Api.ChatAdminWithInvites]? if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ChatAdminWithInvites.self) } - var _4: [Api.User]? + var _2: [Api.User]? if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - var _5: Api.updates.State? - if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.updates.State + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.messages.PeerDialogs.peerDialogs(dialogs: _1!, messages: _2!, chats: _3!, users: _4!, state: _5!) + if _c1 && _c2 { + return Api.messages.ChatAdminsWithInvites.chatAdminsWithInvites(admins: _1!, users: _2!) } else { return nil @@ -1005,16 +943,16 @@ public extension Api.messages { } } public extension Api.messages { - enum PeerSettings: TypeConstructorDescription { - case peerSettings(settings: Api.PeerSettings, chats: [Api.Chat], users: [Api.User]) + enum ChatFull: TypeConstructorDescription { + case chatFull(fullChat: Api.ChatFull, chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .peerSettings(let settings, let chats, let users): + case .chatFull(let fullChat, let chats, let users): if boxed { - buffer.appendInt32(1753266509) + buffer.appendInt32(-438840932) } - settings.serialize(buffer, true) + fullChat.serialize(buffer, true) buffer.appendInt32(481674261) buffer.appendInt32(Int32(chats.count)) for item in chats { @@ -1031,15 +969,15 @@ public extension Api.messages { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .peerSettings(let settings, let chats, let users): - return ("peerSettings", [("settings", settings as Any), ("chats", chats as Any), ("users", users as Any)]) + case .chatFull(let fullChat, let chats, let users): + return ("chatFull", [("fullChat", fullChat as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_peerSettings(_ reader: BufferReader) -> PeerSettings? { - var _1: Api.PeerSettings? + public static func parse_chatFull(_ reader: BufferReader) -> ChatFull? { + var _1: Api.ChatFull? if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.PeerSettings + _1 = Api.parse(reader, signature: signature) as? Api.ChatFull } var _2: [Api.Chat]? if let _ = reader.readInt32() { @@ -1053,7 +991,7 @@ public extension Api.messages { let _c2 = _2 != nil let _c3 = _3 != nil if _c1 && _c2 && _c3 { - return Api.messages.PeerSettings.peerSettings(settings: _1!, chats: _2!, users: _3!) + return Api.messages.ChatFull.chatFull(fullChat: _1!, chats: _2!, users: _3!) } else { return nil @@ -1063,237 +1001,229 @@ public extension Api.messages { } } public extension Api.messages { - enum Reactions: TypeConstructorDescription { - case reactions(hash: Int64, reactions: [Api.Reaction]) - case reactionsNotModified + enum ChatInviteImporters: TypeConstructorDescription { + case chatInviteImporters(count: Int32, importers: [Api.ChatInviteImporter], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .reactions(let hash, let reactions): + case .chatInviteImporters(let count, let importers, let users): if boxed { - buffer.appendInt32(-352454890) + buffer.appendInt32(-2118733814) } - serializeInt64(hash, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(reactions.count)) - for item in reactions { + buffer.appendInt32(Int32(importers.count)) + for item in importers { item.serialize(buffer, true) } - break - case .reactionsNotModified: - if boxed { - buffer.appendInt32(-1334846497) + 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 .reactions(let hash, let reactions): - return ("reactions", [("hash", hash as Any), ("reactions", reactions as Any)]) - case .reactionsNotModified: - return ("reactionsNotModified", []) + case .chatInviteImporters(let count, let importers, let users): + return ("chatInviteImporters", [("count", count as Any), ("importers", importers as Any), ("users", users as Any)]) } } - public static func parse_reactions(_ reader: BufferReader) -> Reactions? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.Reaction]? + public static func parse_chatInviteImporters(_ reader: BufferReader) -> ChatInviteImporters? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.ChatInviteImporter]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Reaction.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ChatInviteImporter.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.Reactions.reactions(hash: _1!, reactions: _2!) + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.messages.ChatInviteImporters.chatInviteImporters(count: _1!, importers: _2!, users: _3!) } else { return nil } } - public static func parse_reactionsNotModified(_ reader: BufferReader) -> Reactions? { - return Api.messages.Reactions.reactionsNotModified - } } } public extension Api.messages { - enum RecentStickers: TypeConstructorDescription { - case recentStickers(hash: Int64, packs: [Api.StickerPack], stickers: [Api.Document], dates: [Int32]) - case recentStickersNotModified + enum Chats: TypeConstructorDescription { + case chats(chats: [Api.Chat]) + case chatsSlice(count: Int32, chats: [Api.Chat]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .recentStickers(let hash, let packs, let stickers, let dates): + case .chats(let chats): if boxed { - buffer.appendInt32(-1999405994) + buffer.appendInt32(1694474197) } - serializeInt64(hash, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(packs.count)) - for item in packs { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(stickers.count)) - for item in stickers { + buffer.appendInt32(Int32(chats.count)) + for item in chats { item.serialize(buffer, true) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(dates.count)) - for item in dates { - serializeInt32(item, buffer: buffer, boxed: false) - } break - case .recentStickersNotModified: + case .chatsSlice(let count, let chats): if boxed { - buffer.appendInt32(186120336) + buffer.appendInt32(-1663561404) + } + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) } - break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .recentStickers(let hash, let packs, let stickers, let dates): - return ("recentStickers", [("hash", hash as Any), ("packs", packs as Any), ("stickers", stickers as Any), ("dates", dates as Any)]) - case .recentStickersNotModified: - return ("recentStickersNotModified", []) + case .chats(let chats): + return ("chats", [("chats", chats as Any)]) + case .chatsSlice(let count, let chats): + return ("chatsSlice", [("count", count as Any), ("chats", chats as Any)]) } } - public static func parse_recentStickers(_ reader: BufferReader) -> RecentStickers? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.StickerPack]? + public static func parse_chats(_ reader: BufferReader) -> Chats? { + var _1: [Api.Chat]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self) + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) } - var _3: [Api.Document]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + let _c1 = _1 != nil + if _c1 { + return Api.messages.Chats.chats(chats: _1!) } - var _4: [Int32]? + else { + return nil + } + } + public static func parse_chatsSlice(_ reader: BufferReader) -> Chats? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.Chat]? if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.RecentStickers.recentStickers(hash: _1!, packs: _2!, stickers: _3!, dates: _4!) + if _c1 && _c2 { + return Api.messages.Chats.chatsSlice(count: _1!, chats: _2!) } else { return nil } } - public static func parse_recentStickersNotModified(_ reader: BufferReader) -> RecentStickers? { - return Api.messages.RecentStickers.recentStickersNotModified - } } } public extension Api.messages { - enum SavedGifs: TypeConstructorDescription { - case savedGifs(hash: Int64, gifs: [Api.Document]) - case savedGifsNotModified + enum CheckedHistoryImportPeer: TypeConstructorDescription { + case checkedHistoryImportPeer(confirmText: String) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .savedGifs(let hash, let gifs): - if boxed { - buffer.appendInt32(-2069878259) - } - serializeInt64(hash, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(gifs.count)) - for item in gifs { - item.serialize(buffer, true) - } - break - case .savedGifsNotModified: + case .checkedHistoryImportPeer(let confirmText): if boxed { - buffer.appendInt32(-402498398) + buffer.appendInt32(-1571952873) } - + serializeString(confirmText, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .savedGifs(let hash, let gifs): - return ("savedGifs", [("hash", hash as Any), ("gifs", gifs as Any)]) - case .savedGifsNotModified: - return ("savedGifsNotModified", []) + case .checkedHistoryImportPeer(let confirmText): + return ("checkedHistoryImportPeer", [("confirmText", confirmText as Any)]) } } - public static func parse_savedGifs(_ reader: BufferReader) -> SavedGifs? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.Document]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) - } + public static func parse_checkedHistoryImportPeer(_ reader: BufferReader) -> CheckedHistoryImportPeer? { + var _1: String? + _1 = parseString(reader) let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.SavedGifs.savedGifs(hash: _1!, gifs: _2!) + if _c1 { + return Api.messages.CheckedHistoryImportPeer.checkedHistoryImportPeer(confirmText: _1!) } else { return nil } } - public static func parse_savedGifsNotModified(_ reader: BufferReader) -> SavedGifs? { - return Api.messages.SavedGifs.savedGifsNotModified - } } } public extension Api.messages { - enum SearchCounter: TypeConstructorDescription { - case searchCounter(flags: Int32, filter: Api.MessagesFilter, count: Int32) + enum DhConfig: TypeConstructorDescription { + case dhConfig(g: Int32, p: Buffer, version: Int32, random: Buffer) + case dhConfigNotModified(random: Buffer) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .searchCounter(let flags, let filter, let count): + case .dhConfig(let g, let p, let version, let random): if boxed { - buffer.appendInt32(-398136321) + buffer.appendInt32(740433629) } - serializeInt32(flags, buffer: buffer, boxed: false) - filter.serialize(buffer, true) - serializeInt32(count, buffer: buffer, boxed: false) + serializeInt32(g, buffer: buffer, boxed: false) + serializeBytes(p, buffer: buffer, boxed: false) + serializeInt32(version, buffer: buffer, boxed: false) + serializeBytes(random, buffer: buffer, boxed: false) + break + case .dhConfigNotModified(let random): + if boxed { + buffer.appendInt32(-1058912715) + } + serializeBytes(random, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .searchCounter(let flags, let filter, let count): - return ("searchCounter", [("flags", flags as Any), ("filter", filter as Any), ("count", count as Any)]) + case .dhConfig(let g, let p, let version, let random): + return ("dhConfig", [("g", g as Any), ("p", p as Any), ("version", version as Any), ("random", random as Any)]) + case .dhConfigNotModified(let random): + return ("dhConfigNotModified", [("random", random as Any)]) } } - public static func parse_searchCounter(_ reader: BufferReader) -> SearchCounter? { + public static func parse_dhConfig(_ reader: BufferReader) -> DhConfig? { var _1: Int32? _1 = reader.readInt32() - var _2: Api.MessagesFilter? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.MessagesFilter - } + var _2: Buffer? + _2 = parseBytes(reader) var _3: Int32? _3 = reader.readInt32() + var _4: Buffer? + _4 = parseBytes(reader) let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.SearchCounter.searchCounter(flags: _1!, filter: _2!, count: _3!) + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.messages.DhConfig.dhConfig(g: _1!, p: _2!, version: _3!, random: _4!) + } + else { + return nil + } + } + public static func parse_dhConfigNotModified(_ reader: BufferReader) -> DhConfig? { + var _1: Buffer? + _1 = parseBytes(reader) + let _c1 = _1 != nil + if _c1 { + return Api.messages.DhConfig.dhConfigNotModified(random: _1!) } else { return nil @@ -1303,23 +1233,52 @@ public extension Api.messages { } } public extension Api.messages { - enum SearchResultsCalendar: TypeConstructorDescription { - case searchResultsCalendar(flags: Int32, count: Int32, minDate: Int32, minMsgId: Int32, offsetIdOffset: Int32?, periods: [Api.SearchResultsCalendarPeriod], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + enum Dialogs: TypeConstructorDescription { + case dialogs(dialogs: [Api.Dialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + case dialogsNotModified(count: Int32) + case dialogsSlice(count: Int32, dialogs: [Api.Dialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .searchResultsCalendar(let flags, let count, let minDate, let minMsgId, let offsetIdOffset, let periods, let messages, let chats, let users): + case .dialogs(let dialogs, let messages, let chats, let users): if boxed { - buffer.appendInt32(343859772) + buffer.appendInt32(364538944) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(dialogs.count)) + for item in dialogs { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + case .dialogsNotModified(let count): + if boxed { + buffer.appendInt32(-253500010) + } + serializeInt32(count, buffer: buffer, boxed: false) + break + case .dialogsSlice(let count, let dialogs, let messages, let chats, let users): + if boxed { + buffer.appendInt32(1910543603) } - serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(count, buffer: buffer, boxed: false) - serializeInt32(minDate, buffer: buffer, boxed: false) - serializeInt32(minMsgId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(offsetIdOffset!, buffer: buffer, boxed: false)} buffer.appendInt32(481674261) - buffer.appendInt32(Int32(periods.count)) - for item in periods { + buffer.appendInt32(Int32(dialogs.count)) + for item in dialogs { item.serialize(buffer, true) } buffer.appendInt32(481674261) @@ -1343,157 +1302,80 @@ public extension Api.messages { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .searchResultsCalendar(let flags, let count, let minDate, let minMsgId, let offsetIdOffset, let periods, let messages, let chats, let users): - return ("searchResultsCalendar", [("flags", flags as Any), ("count", count as Any), ("minDate", minDate as Any), ("minMsgId", minMsgId as Any), ("offsetIdOffset", offsetIdOffset as Any), ("periods", periods as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + case .dialogs(let dialogs, let messages, let chats, let users): + return ("dialogs", [("dialogs", dialogs as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + case .dialogsNotModified(let count): + return ("dialogsNotModified", [("count", count as Any)]) + case .dialogsSlice(let count, let dialogs, let messages, let chats, let users): + return ("dialogsSlice", [("count", count as Any), ("dialogs", dialogs as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_searchResultsCalendar(_ reader: BufferReader) -> SearchResultsCalendar? { - 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? - if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() } - var _6: [Api.SearchResultsCalendarPeriod]? + public static func parse_dialogs(_ reader: BufferReader) -> Dialogs? { + var _1: [Api.Dialog]? if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SearchResultsCalendarPeriod.self) + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Dialog.self) } - var _7: [Api.Message]? + var _2: [Api.Message]? if let _ = reader.readInt32() { - _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) } - var _8: [Api.Chat]? + var _3: [Api.Chat]? if let _ = reader.readInt32() { - _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) } - var _9: [Api.User]? + var _4: [Api.User]? if let _ = reader.readInt32() { - _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - let _c8 = _8 != nil - let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.messages.SearchResultsCalendar.searchResultsCalendar(flags: _1!, count: _2!, minDate: _3!, minMsgId: _4!, offsetIdOffset: _5, periods: _6!, messages: _7!, chats: _8!, users: _9!) + if _c1 && _c2 && _c3 && _c4 { + return Api.messages.Dialogs.dialogs(dialogs: _1!, messages: _2!, chats: _3!, users: _4!) } else { return nil } } - - } -} -public extension Api.messages { - enum SearchResultsPositions: TypeConstructorDescription { - case searchResultsPositions(count: Int32, positions: [Api.SearchResultsPosition]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .searchResultsPositions(let count, let positions): - if boxed { - buffer.appendInt32(1404185519) - } - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(positions.count)) - for item in positions { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .searchResultsPositions(let count, let positions): - return ("searchResultsPositions", [("count", count as Any), ("positions", positions as Any)]) - } - } - - public static func parse_searchResultsPositions(_ reader: BufferReader) -> SearchResultsPositions? { + public static func parse_dialogsNotModified(_ reader: BufferReader) -> Dialogs? { var _1: Int32? _1 = reader.readInt32() - var _2: [Api.SearchResultsPosition]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SearchResultsPosition.self) - } let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.SearchResultsPositions.searchResultsPositions(count: _1!, positions: _2!) + if _c1 { + return Api.messages.Dialogs.dialogsNotModified(count: _1!) } else { return nil } } - - } -} -public extension Api.messages { - enum SentEncryptedMessage: TypeConstructorDescription { - case sentEncryptedFile(date: Int32, file: Api.EncryptedFile) - case sentEncryptedMessage(date: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .sentEncryptedFile(let date, let file): - if boxed { - buffer.appendInt32(-1802240206) - } - serializeInt32(date, buffer: buffer, boxed: false) - file.serialize(buffer, true) - break - case .sentEncryptedMessage(let date): - if boxed { - buffer.appendInt32(1443858741) - } - serializeInt32(date, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .sentEncryptedFile(let date, let file): - return ("sentEncryptedFile", [("date", date as Any), ("file", file as Any)]) - case .sentEncryptedMessage(let date): - return ("sentEncryptedMessage", [("date", date as Any)]) - } - } - - public static func parse_sentEncryptedFile(_ reader: BufferReader) -> SentEncryptedMessage? { + public static func parse_dialogsSlice(_ reader: BufferReader) -> Dialogs? { var _1: Int32? _1 = reader.readInt32() - var _2: Api.EncryptedFile? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.EncryptedFile + var _2: [Api.Dialog]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Dialog.self) } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.SentEncryptedMessage.sentEncryptedFile(date: _1!, file: _2!) + var _3: [Api.Message]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) } - else { - return nil + 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) } - } - public static func parse_sentEncryptedMessage(_ reader: BufferReader) -> SentEncryptedMessage? { - var _1: Int32? - _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.messages.SentEncryptedMessage.sentEncryptedMessage(date: _1!) + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.messages.Dialogs.dialogsSlice(count: _1!, dialogs: _2!, messages: _3!, chats: _4!, users: _5!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api28.swift b/submodules/TelegramApi/Sources/Api28.swift index 15ed48860f2..afd23f9c90c 100644 --- a/submodules/TelegramApi/Sources/Api28.swift +++ b/submodules/TelegramApi/Sources/Api28.swift @@ -1,21 +1,23 @@ public extension Api.messages { - enum SponsoredMessages: TypeConstructorDescription { - case sponsoredMessages(flags: Int32, postsBetween: Int32?, messages: [Api.SponsoredMessage], chats: [Api.Chat], users: [Api.User]) - case sponsoredMessagesEmpty + enum DiscussionMessage: TypeConstructorDescription { + case discussionMessage(flags: Int32, messages: [Api.Message], maxId: Int32?, readInboxMaxId: Int32?, readOutboxMaxId: Int32?, unreadCount: Int32, chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .sponsoredMessages(let flags, let postsBetween, let messages, let chats, let users): + case .discussionMessage(let flags, let messages, let maxId, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chats, let users): if boxed { - buffer.appendInt32(-907141753) + buffer.appendInt32(-1506535550) } serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(postsBetween!, buffer: buffer, boxed: false)} buffer.appendInt32(481674261) buffer.appendInt32(Int32(messages.count)) for item in messages { item.serialize(buffer, true) } + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(maxId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(readInboxMaxId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeInt32(readOutboxMaxId!, buffer: buffer, boxed: false)} + serializeInt32(unreadCount, buffer: buffer, boxed: false) buffer.appendInt32(481674261) buffer.appendInt32(Int32(chats.count)) for item in chats { @@ -26,91 +28,79 @@ public extension Api.messages { for item in users { item.serialize(buffer, true) } - break - case .sponsoredMessagesEmpty: - if boxed { - buffer.appendInt32(406407439) - } - break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .sponsoredMessages(let flags, let postsBetween, let messages, let chats, let users): - return ("sponsoredMessages", [("flags", flags as Any), ("postsBetween", postsBetween as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) - case .sponsoredMessagesEmpty: - return ("sponsoredMessagesEmpty", []) + case .discussionMessage(let flags, let messages, let maxId, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chats, let users): + return ("discussionMessage", [("flags", flags as Any), ("messages", messages as Any), ("maxId", maxId as Any), ("readInboxMaxId", readInboxMaxId as Any), ("readOutboxMaxId", readOutboxMaxId as Any), ("unreadCount", unreadCount as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_sponsoredMessages(_ reader: BufferReader) -> SponsoredMessages? { + public static func parse_discussionMessage(_ reader: BufferReader) -> DiscussionMessage? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } - var _3: [Api.SponsoredMessage]? + var _2: [Api.Message]? if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SponsoredMessage.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) } - var _4: [Api.Chat]? + var _3: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } + var _4: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_4 = reader.readInt32() } + var _5: Int32? + if Int(_1!) & Int(1 << 2) != 0 {_5 = reader.readInt32() } + var _6: Int32? + _6 = reader.readInt32() + var _7: [Api.Chat]? if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) } - var _5: [Api.User]? + var _8: [Api.User]? if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } 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 - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.messages.SponsoredMessages.sponsoredMessages(flags: _1!, postsBetween: _2, messages: _3!, chats: _4!, users: _5!) + 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 = _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { + return Api.messages.DiscussionMessage.discussionMessage(flags: _1!, messages: _2!, maxId: _3, readInboxMaxId: _4, readOutboxMaxId: _5, unreadCount: _6!, chats: _7!, users: _8!) } else { return nil } } - public static func parse_sponsoredMessagesEmpty(_ reader: BufferReader) -> SponsoredMessages? { - return Api.messages.SponsoredMessages.sponsoredMessagesEmpty - } } } public extension Api.messages { - enum StickerSet: TypeConstructorDescription { - case stickerSet(set: Api.StickerSet, packs: [Api.StickerPack], keywords: [Api.StickerKeyword], documents: [Api.Document]) - case stickerSetNotModified + enum EmojiGroups: TypeConstructorDescription { + case emojiGroups(hash: Int32, groups: [Api.EmojiGroup]) + case emojiGroupsNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .stickerSet(let set, let packs, let keywords, let documents): + case .emojiGroups(let hash, let groups): if boxed { - buffer.appendInt32(1846886166) - } - set.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(packs.count)) - for item in packs { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(keywords.count)) - for item in keywords { - item.serialize(buffer, true) + buffer.appendInt32(-2011186869) } + serializeInt32(hash, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(documents.count)) - for item in documents { + buffer.appendInt32(Int32(groups.count)) + for item in groups { item.serialize(buffer, true) } break - case .stickerSetNotModified: + case .emojiGroupsNotModified: if boxed { - buffer.appendInt32(-738646805) + buffer.appendInt32(1874111879) } break @@ -119,195 +109,169 @@ public extension Api.messages { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .stickerSet(let set, let packs, let keywords, let documents): - return ("stickerSet", [("set", set as Any), ("packs", packs as Any), ("keywords", keywords as Any), ("documents", documents as Any)]) - case .stickerSetNotModified: - return ("stickerSetNotModified", []) + case .emojiGroups(let hash, let groups): + return ("emojiGroups", [("hash", hash as Any), ("groups", groups as Any)]) + case .emojiGroupsNotModified: + return ("emojiGroupsNotModified", []) } } - public static func parse_stickerSet(_ reader: BufferReader) -> StickerSet? { - var _1: Api.StickerSet? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.StickerSet - } - var _2: [Api.StickerPack]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self) - } - var _3: [Api.StickerKeyword]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerKeyword.self) - } - var _4: [Api.Document]? + public static func parse_emojiGroups(_ reader: BufferReader) -> EmojiGroups? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.EmojiGroup]? if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.EmojiGroup.self) } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.StickerSet.stickerSet(set: _1!, packs: _2!, keywords: _3!, documents: _4!) + if _c1 && _c2 { + return Api.messages.EmojiGroups.emojiGroups(hash: _1!, groups: _2!) } else { return nil } } - public static func parse_stickerSetNotModified(_ reader: BufferReader) -> StickerSet? { - return Api.messages.StickerSet.stickerSetNotModified + public static func parse_emojiGroupsNotModified(_ reader: BufferReader) -> EmojiGroups? { + return Api.messages.EmojiGroups.emojiGroupsNotModified } } } public extension Api.messages { - enum StickerSetInstallResult: TypeConstructorDescription { - case stickerSetInstallResultArchive(sets: [Api.StickerSetCovered]) - case stickerSetInstallResultSuccess + enum ExportedChatInvite: TypeConstructorDescription { + case exportedChatInvite(invite: Api.ExportedChatInvite, users: [Api.User]) + case exportedChatInviteReplaced(invite: Api.ExportedChatInvite, newInvite: Api.ExportedChatInvite, users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .stickerSetInstallResultArchive(let sets): + case .exportedChatInvite(let invite, let users): if boxed { - buffer.appendInt32(904138920) + buffer.appendInt32(410107472) } + invite.serialize(buffer, true) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(sets.count)) - for item in sets { + buffer.appendInt32(Int32(users.count)) + for item in users { item.serialize(buffer, true) } break - case .stickerSetInstallResultSuccess: + case .exportedChatInviteReplaced(let invite, let newInvite, let users): if boxed { - buffer.appendInt32(946083368) + buffer.appendInt32(572915951) + } + invite.serialize(buffer, true) + newInvite.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) } - break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .stickerSetInstallResultArchive(let sets): - return ("stickerSetInstallResultArchive", [("sets", sets as Any)]) - case .stickerSetInstallResultSuccess: - return ("stickerSetInstallResultSuccess", []) + case .exportedChatInvite(let invite, let users): + return ("exportedChatInvite", [("invite", invite as Any), ("users", users as Any)]) + case .exportedChatInviteReplaced(let invite, let newInvite, let users): + return ("exportedChatInviteReplaced", [("invite", invite as Any), ("newInvite", newInvite as Any), ("users", users as Any)]) } } - public static func parse_stickerSetInstallResultArchive(_ reader: BufferReader) -> StickerSetInstallResult? { - var _1: [Api.StickerSetCovered]? + public static func parse_exportedChatInvite(_ reader: BufferReader) -> ExportedChatInvite? { + var _1: Api.ExportedChatInvite? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite + } + var _2: [Api.User]? if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil - if _c1 { - return Api.messages.StickerSetInstallResult.stickerSetInstallResultArchive(sets: _1!) + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.messages.ExportedChatInvite.exportedChatInvite(invite: _1!, users: _2!) } else { return nil } } - public static func parse_stickerSetInstallResultSuccess(_ reader: BufferReader) -> StickerSetInstallResult? { - return Api.messages.StickerSetInstallResult.stickerSetInstallResultSuccess - } - - } -} -public extension Api.messages { - enum Stickers: TypeConstructorDescription { - case stickers(hash: Int64, stickers: [Api.Document]) - case stickersNotModified - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .stickers(let hash, let stickers): - if boxed { - buffer.appendInt32(816245886) - } - serializeInt64(hash, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(stickers.count)) - for item in stickers { - item.serialize(buffer, true) - } - break - case .stickersNotModified: - if boxed { - buffer.appendInt32(-244016606) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .stickers(let hash, let stickers): - return ("stickers", [("hash", hash as Any), ("stickers", stickers as Any)]) - case .stickersNotModified: - return ("stickersNotModified", []) - } - } - - public static func parse_stickers(_ reader: BufferReader) -> Stickers? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.Document]? + public static func parse_exportedChatInviteReplaced(_ reader: BufferReader) -> ExportedChatInvite? { + var _1: Api.ExportedChatInvite? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite + } + var _2: Api.ExportedChatInvite? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite + } + var _3: [Api.User]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.Stickers.stickers(hash: _1!, stickers: _2!) + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.messages.ExportedChatInvite.exportedChatInviteReplaced(invite: _1!, newInvite: _2!, users: _3!) } else { return nil } } - public static func parse_stickersNotModified(_ reader: BufferReader) -> Stickers? { - return Api.messages.Stickers.stickersNotModified - } } } public extension Api.messages { - enum TranscribedAudio: TypeConstructorDescription { - case transcribedAudio(flags: Int32, transcriptionId: Int64, text: String) + enum ExportedChatInvites: TypeConstructorDescription { + case exportedChatInvites(count: Int32, invites: [Api.ExportedChatInvite], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .transcribedAudio(let flags, let transcriptionId, let text): + case .exportedChatInvites(let count, let invites, let users): if boxed { - buffer.appendInt32(-1821037486) + buffer.appendInt32(-1111085620) + } + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(invites.count)) + for item in invites { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(transcriptionId, buffer: buffer, boxed: false) - serializeString(text, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .transcribedAudio(let flags, let transcriptionId, let text): - return ("transcribedAudio", [("flags", flags as Any), ("transcriptionId", transcriptionId as Any), ("text", text as Any)]) + case .exportedChatInvites(let count, let invites, let users): + return ("exportedChatInvites", [("count", count as Any), ("invites", invites as Any), ("users", users as Any)]) } } - public static func parse_transcribedAudio(_ reader: BufferReader) -> TranscribedAudio? { + public static func parse_exportedChatInvites(_ reader: BufferReader) -> ExportedChatInvites? { var _1: Int32? _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: String? - _3 = parseString(reader) + var _2: [Api.ExportedChatInvite]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ExportedChatInvite.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil if _c1 && _c2 && _c3 { - return Api.messages.TranscribedAudio.transcribedAudio(flags: _1!, transcriptionId: _2!, text: _3!) + return Api.messages.ExportedChatInvites.exportedChatInvites(count: _1!, invites: _2!, users: _3!) } else { return nil @@ -317,113 +281,149 @@ public extension Api.messages { } } public extension Api.messages { - enum TranslatedText: TypeConstructorDescription { - case translateResult(result: [Api.TextWithEntities]) + enum FavedStickers: TypeConstructorDescription { + case favedStickers(hash: Int64, packs: [Api.StickerPack], stickers: [Api.Document]) + case favedStickersNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .translateResult(let result): + case .favedStickers(let hash, let packs, let stickers): if boxed { - buffer.appendInt32(870003448) + buffer.appendInt32(750063767) + } + serializeInt64(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(packs.count)) + for item in packs { + item.serialize(buffer, true) } buffer.appendInt32(481674261) - buffer.appendInt32(Int32(result.count)) - for item in result { + buffer.appendInt32(Int32(stickers.count)) + for item in stickers { item.serialize(buffer, true) } + break + case .favedStickersNotModified: + if boxed { + buffer.appendInt32(-1634752813) + } + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .translateResult(let result): - return ("translateResult", [("result", result as Any)]) + case .favedStickers(let hash, let packs, let stickers): + return ("favedStickers", [("hash", hash as Any), ("packs", packs as Any), ("stickers", stickers as Any)]) + case .favedStickersNotModified: + return ("favedStickersNotModified", []) } } - public static func parse_translateResult(_ reader: BufferReader) -> TranslatedText? { - var _1: [Api.TextWithEntities]? + public static func parse_favedStickers(_ reader: BufferReader) -> FavedStickers? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.StickerPack]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self) + } + var _3: [Api.Document]? if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.TextWithEntities.self) + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) } let _c1 = _1 != nil - if _c1 { - return Api.messages.TranslatedText.translateResult(result: _1!) + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.messages.FavedStickers.favedStickers(hash: _1!, packs: _2!, stickers: _3!) } else { return nil } } + public static func parse_favedStickersNotModified(_ reader: BufferReader) -> FavedStickers? { + return Api.messages.FavedStickers.favedStickersNotModified + } } } public extension Api.messages { - enum VotesList: TypeConstructorDescription { - case votesList(flags: Int32, count: Int32, votes: [Api.MessagePeerVote], chats: [Api.Chat], users: [Api.User], nextOffset: String?) + enum FeaturedStickers: TypeConstructorDescription { + case featuredStickers(flags: Int32, hash: Int64, count: Int32, sets: [Api.StickerSetCovered], unread: [Int64]) + case featuredStickersNotModified(count: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .votesList(let flags, let count, let votes, let chats, let users, let nextOffset): + case .featuredStickers(let flags, let hash, let count, let sets, let unread): if boxed { - buffer.appendInt32(1218005070) + buffer.appendInt32(-1103615738) } serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) serializeInt32(count, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(votes.count)) - for item in votes { + buffer.appendInt32(Int32(sets.count)) + for item in sets { item.serialize(buffer, true) } buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) + buffer.appendInt32(Int32(unread.count)) + for item in unread { + serializeInt64(item, buffer: buffer, boxed: false) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + break + case .featuredStickersNotModified(let count): + if boxed { + buffer.appendInt32(-958657434) } - if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} + serializeInt32(count, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .votesList(let flags, let count, let votes, let chats, let users, let nextOffset): - return ("votesList", [("flags", flags as Any), ("count", count as Any), ("votes", votes as Any), ("chats", chats as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)]) + case .featuredStickers(let flags, let hash, let count, let sets, let unread): + return ("featuredStickers", [("flags", flags as Any), ("hash", hash as Any), ("count", count as Any), ("sets", sets as Any), ("unread", unread as Any)]) + case .featuredStickersNotModified(let count): + return ("featuredStickersNotModified", [("count", count as Any)]) } } - public static func parse_votesList(_ reader: BufferReader) -> VotesList? { + public static func parse_featuredStickers(_ reader: BufferReader) -> FeaturedStickers? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: [Api.MessagePeerVote]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessagePeerVote.self) - } - var _4: [Api.Chat]? + var _2: Int64? + _2 = reader.readInt64() + var _3: Int32? + _3 = reader.readInt32() + var _4: [Api.StickerSetCovered]? if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self) } - var _5: [Api.User]? + var _5: [Int64]? if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _5 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) } - var _6: String? - if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.messages.VotesList.votesList(flags: _1!, count: _2!, votes: _3!, chats: _4!, users: _5!, nextOffset: _6) + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.messages.FeaturedStickers.featuredStickers(flags: _1!, hash: _2!, count: _3!, sets: _4!, unread: _5!) + } + else { + return nil + } + } + public static func parse_featuredStickersNotModified(_ reader: BufferReader) -> FeaturedStickers? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.messages.FeaturedStickers.featuredStickersNotModified(count: _1!) } else { return nil @@ -433,16 +433,27 @@ public extension Api.messages { } } public extension Api.messages { - enum WebPage: TypeConstructorDescription { - case webPage(webpage: Api.WebPage, chats: [Api.Chat], users: [Api.User]) + enum ForumTopics: TypeConstructorDescription { + case forumTopics(flags: Int32, count: Int32, topics: [Api.ForumTopic], messages: [Api.Message], chats: [Api.Chat], users: [Api.User], pts: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .webPage(let webpage, let chats, let users): + case .forumTopics(let flags, let count, let topics, let messages, let chats, let users, let pts): if boxed { - buffer.appendInt32(-44166467) + buffer.appendInt32(913709011) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(topics.count)) + for item in topics { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) } - webpage.serialize(buffer, true) buffer.appendInt32(481674261) buffer.appendInt32(Int32(chats.count)) for item in chats { @@ -453,35 +464,50 @@ public extension Api.messages { for item in users { item.serialize(buffer, true) } + serializeInt32(pts, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .webPage(let webpage, let chats, let users): - return ("webPage", [("webpage", webpage as Any), ("chats", chats as Any), ("users", users as Any)]) + case .forumTopics(let flags, let count, let topics, let messages, let chats, let users, let pts): + return ("forumTopics", [("flags", flags as Any), ("count", count as Any), ("topics", topics as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any), ("pts", pts as Any)]) } } - public static func parse_webPage(_ reader: BufferReader) -> WebPage? { - var _1: Api.WebPage? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.WebPage + public static func parse_forumTopics(_ reader: BufferReader) -> ForumTopics? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: [Api.ForumTopic]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ForumTopic.self) } - var _2: [Api.Chat]? + var _4: [Api.Message]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) } - var _3: [Api.User]? + var _5: [Api.Chat]? if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _6: [Api.User]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } + var _7: Int32? + _7 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.WebPage.webPage(webpage: _1!, chats: _2!, users: _3!) + 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.messages.ForumTopics.forumTopics(flags: _1!, count: _2!, topics: _3!, messages: _4!, chats: _5!, users: _6!, pts: _7!) } else { return nil @@ -490,72 +516,77 @@ public extension Api.messages { } } -public extension Api.payments { - enum BankCardData: TypeConstructorDescription { - case bankCardData(title: String, openUrls: [Api.BankCardOpenUrl]) +public extension Api.messages { + enum FoundStickerSets: TypeConstructorDescription { + case foundStickerSets(hash: Int64, sets: [Api.StickerSetCovered]) + case foundStickerSetsNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .bankCardData(let title, let openUrls): + case .foundStickerSets(let hash, let sets): if boxed { - buffer.appendInt32(1042605427) + buffer.appendInt32(-1963942446) } - serializeString(title, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(openUrls.count)) - for item in openUrls { + buffer.appendInt32(Int32(sets.count)) + for item in sets { item.serialize(buffer, true) } + break + case .foundStickerSetsNotModified: + if boxed { + buffer.appendInt32(223655517) + } + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .bankCardData(let title, let openUrls): - return ("bankCardData", [("title", title as Any), ("openUrls", openUrls as Any)]) + case .foundStickerSets(let hash, let sets): + return ("foundStickerSets", [("hash", hash as Any), ("sets", sets as Any)]) + case .foundStickerSetsNotModified: + return ("foundStickerSetsNotModified", []) } } - public static func parse_bankCardData(_ reader: BufferReader) -> BankCardData? { - var _1: String? - _1 = parseString(reader) - var _2: [Api.BankCardOpenUrl]? + public static func parse_foundStickerSets(_ reader: BufferReader) -> FoundStickerSets? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.StickerSetCovered]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.BankCardOpenUrl.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self) } let _c1 = _1 != nil let _c2 = _2 != nil if _c1 && _c2 { - return Api.payments.BankCardData.bankCardData(title: _1!, openUrls: _2!) + return Api.messages.FoundStickerSets.foundStickerSets(hash: _1!, sets: _2!) } else { return nil } } + public static func parse_foundStickerSetsNotModified(_ reader: BufferReader) -> FoundStickerSets? { + return Api.messages.FoundStickerSets.foundStickerSetsNotModified + } } } -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]) +public extension Api.messages { + enum HighScores: TypeConstructorDescription { + case highScores(scores: [Api.HighScore], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .checkedGiftCode(let flags, let fromId, let giveawayMsgId, let toId, let date, let months, let usedDate, let chats, let users): + case .highScores(let scores, let users): if boxed { - buffer.appendInt32(-1222446760) + buffer.appendInt32(-1707344487) } - serializeInt32(flags, buffer: buffer, boxed: false) - fromId.serialize(buffer, true) - if Int(flags) & Int(1 << 3) != 0 {serializeInt32(giveawayMsgId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 0) != 0 {serializeInt64(toId!, buffer: buffer, boxed: false)} - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt32(months, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(usedDate!, buffer: buffer, boxed: false)} buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { + buffer.appendInt32(Int32(scores.count)) + for item in scores { item.serialize(buffer, true) } buffer.appendInt32(481674261) @@ -569,47 +600,24 @@ public extension Api.payments { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .checkedGiftCode(let flags, let fromId, let giveawayMsgId, let toId, let date, let months, let usedDate, let chats, let users): - return ("checkedGiftCode", [("flags", flags as Any), ("fromId", fromId as Any), ("giveawayMsgId", giveawayMsgId as Any), ("toId", toId as Any), ("date", date as Any), ("months", months as Any), ("usedDate", usedDate as Any), ("chats", chats as Any), ("users", users as Any)]) + case .highScores(let scores, let users): + return ("highScores", [("scores", scores as Any), ("users", users as Any)]) } } - public static func parse_checkedGiftCode(_ reader: BufferReader) -> CheckedGiftCode? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.Peer? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _3: Int32? - if Int(_1!) & Int(1 << 3) != 0 {_3 = reader.readInt32() } - var _4: Int64? - if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt64() } - var _5: Int32? - _5 = reader.readInt32() - var _6: Int32? - _6 = reader.readInt32() - var _7: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_7 = reader.readInt32() } - var _8: [Api.Chat]? + public static func parse_highScores(_ reader: BufferReader) -> HighScores? { + var _1: [Api.HighScore]? if let _ = reader.readInt32() { - _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.HighScore.self) } - var _9: [Api.User]? + var _2: [Api.User]? if let _ = reader.readInt32() { - _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 3) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil - let _c8 = _8 != nil - let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.payments.CheckedGiftCode.checkedGiftCode(flags: _1!, fromId: _2!, giveawayMsgId: _3, toId: _4, date: _5!, months: _6!, usedDate: _7, chats: _8!, users: _9!) + if _c1 && _c2 { + return Api.messages.HighScores.highScores(scores: _1!, users: _2!) } else { return nil @@ -618,34 +626,34 @@ public extension Api.payments { } } -public extension Api.payments { - enum ExportedInvoice: TypeConstructorDescription { - case exportedInvoice(url: String) +public extension Api.messages { + enum HistoryImport: TypeConstructorDescription { + case historyImport(id: Int64) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .exportedInvoice(let url): + case .historyImport(let id): if boxed { - buffer.appendInt32(-1362048039) + buffer.appendInt32(375566091) } - serializeString(url, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .exportedInvoice(let url): - return ("exportedInvoice", [("url", url as Any)]) + case .historyImport(let id): + return ("historyImport", [("id", id as Any)]) } } - public static func parse_exportedInvoice(_ reader: BufferReader) -> ExportedInvoice? { - var _1: String? - _1 = parseString(reader) + public static func parse_historyImport(_ reader: BufferReader) -> HistoryImport? { + var _1: Int64? + _1 = reader.readInt64() let _c1 = _1 != nil if _c1 { - return Api.payments.ExportedInvoice.exportedInvoice(url: _1!) + return Api.messages.HistoryImport.historyImport(id: _1!) } else { return nil @@ -654,90 +662,38 @@ public extension Api.payments { } } -public extension Api.payments { - enum GiveawayInfo: TypeConstructorDescription { - case giveawayInfo(flags: Int32, startDate: Int32, joinedTooEarlyDate: Int32?, adminDisallowedChatId: Int64?, disallowedCountry: String?) - case giveawayInfoResults(flags: Int32, startDate: Int32, giftCodeSlug: String?, finishDate: Int32, winnersCount: Int32, activatedCount: Int32) +public extension Api.messages { + enum HistoryImportParsed: TypeConstructorDescription { + case historyImportParsed(flags: Int32, title: String?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .giveawayInfo(let flags, let startDate, let joinedTooEarlyDate, let adminDisallowedChatId, let disallowedCountry): + case .historyImportParsed(let flags, let title): if boxed { - buffer.appendInt32(1130879648) + buffer.appendInt32(1578088377) } serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(startDate, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(joinedTooEarlyDate!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeInt64(adminDisallowedChatId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {serializeString(disallowedCountry!, buffer: buffer, boxed: false)} - break - case .giveawayInfoResults(let flags, let startDate, let giftCodeSlug, let finishDate, let winnersCount, let activatedCount): - if boxed { - buffer.appendInt32(13456752) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(startDate, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(giftCodeSlug!, buffer: buffer, boxed: false)} - serializeInt32(finishDate, buffer: buffer, boxed: false) - serializeInt32(winnersCount, buffer: buffer, boxed: false) - serializeInt32(activatedCount, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {serializeString(title!, buffer: buffer, boxed: false)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .giveawayInfo(let flags, let startDate, let joinedTooEarlyDate, let adminDisallowedChatId, let disallowedCountry): - return ("giveawayInfo", [("flags", flags as Any), ("startDate", startDate as Any), ("joinedTooEarlyDate", joinedTooEarlyDate as Any), ("adminDisallowedChatId", adminDisallowedChatId as Any), ("disallowedCountry", disallowedCountry as Any)]) - case .giveawayInfoResults(let flags, let startDate, let giftCodeSlug, let finishDate, let winnersCount, let activatedCount): - return ("giveawayInfoResults", [("flags", flags as Any), ("startDate", startDate as Any), ("giftCodeSlug", giftCodeSlug as Any), ("finishDate", finishDate as Any), ("winnersCount", winnersCount as Any), ("activatedCount", activatedCount as Any)]) + case .historyImportParsed(let flags, let title): + return ("historyImportParsed", [("flags", flags as Any), ("title", title as Any)]) } } - public static func parse_giveawayInfo(_ reader: BufferReader) -> GiveawayInfo? { + public static func parse_historyImportParsed(_ reader: BufferReader) -> HistoryImportParsed? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() } - var _4: Int64? - if Int(_1!) & Int(1 << 2) != 0 {_4 = reader.readInt64() } - var _5: String? - if Int(_1!) & Int(1 << 4) != 0 {_5 = parseString(reader) } + var _2: String? + if Int(_1!) & Int(1 << 2) != 0 {_2 = parseString(reader) } let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.payments.GiveawayInfo.giveawayInfo(flags: _1!, startDate: _2!, joinedTooEarlyDate: _3, adminDisallowedChatId: _4, disallowedCountry: _5) - } - else { - return nil - } - } - public static func parse_giveawayInfoResults(_ reader: BufferReader) -> GiveawayInfo? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: String? - if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } - var _4: Int32? - _4 = reader.readInt32() - var _5: Int32? - _5 = reader.readInt32() - var _6: Int32? - _6 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.payments.GiveawayInfo.giveawayInfoResults(flags: _1!, startDate: _2!, giftCodeSlug: _3, finishDate: _4!, winnersCount: _5!, activatedCount: _6!) + let _c2 = (Int(_1!) & Int(1 << 2) == 0) || _2 != nil + if _c1 && _c2 { + return Api.messages.HistoryImportParsed.historyImportParsed(flags: _1!, title: _2) } else { return nil @@ -746,38 +702,26 @@ public extension Api.payments { } } -public extension Api.payments { - enum PaymentForm: TypeConstructorDescription { - case paymentForm(flags: Int32, formId: Int64, botId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, providerId: Int64, url: String, nativeProvider: String?, nativeParams: Api.DataJSON?, additionalMethods: [Api.PaymentFormMethod]?, savedInfo: Api.PaymentRequestedInfo?, savedCredentials: [Api.PaymentSavedCredentials]?, users: [Api.User]) +public extension Api.messages { + enum InactiveChats: TypeConstructorDescription { + case inactiveChats(dates: [Int32], chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .paymentForm(let flags, let formId, let botId, let title, let description, let photo, let invoice, let providerId, let url, let nativeProvider, let nativeParams, let additionalMethods, let savedInfo, let savedCredentials, let users): + case .inactiveChats(let dates, let chats, let users): if boxed { - buffer.appendInt32(-1610250415) + buffer.appendInt32(-1456996667) } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(formId, buffer: buffer, boxed: false) - serializeInt64(botId, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - serializeString(description, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 5) != 0 {photo!.serialize(buffer, true)} - invoice.serialize(buffer, true) - serializeInt64(providerId, buffer: buffer, boxed: false) - serializeString(url, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 4) != 0 {serializeString(nativeProvider!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {nativeParams!.serialize(buffer, true)} - if Int(flags) & Int(1 << 6) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(additionalMethods!.count)) - for item in additionalMethods! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 0) != 0 {savedInfo!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(savedCredentials!.count)) - for item in savedCredentials! { + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(dates.count)) + for item in dates { + serializeInt32(item, buffer: buffer, boxed: false) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { item.serialize(buffer, true) - }} + } buffer.appendInt32(481674261) buffer.appendInt32(Int32(users.count)) for item in users { @@ -789,73 +733,29 @@ public extension Api.payments { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .paymentForm(let flags, let formId, let botId, let title, let description, let photo, let invoice, let providerId, let url, let nativeProvider, let nativeParams, let additionalMethods, let savedInfo, let savedCredentials, let users): - return ("paymentForm", [("flags", flags as Any), ("formId", formId as Any), ("botId", botId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("providerId", providerId as Any), ("url", url as Any), ("nativeProvider", nativeProvider as Any), ("nativeParams", nativeParams as Any), ("additionalMethods", additionalMethods as Any), ("savedInfo", savedInfo as Any), ("savedCredentials", savedCredentials as Any), ("users", users as Any)]) + case .inactiveChats(let dates, let chats, let users): + return ("inactiveChats", [("dates", dates as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_paymentForm(_ reader: BufferReader) -> PaymentForm? { - 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.WebDocument? - if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.WebDocument - } } - var _7: Api.Invoice? - if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.Invoice - } - var _8: Int64? - _8 = reader.readInt64() - var _9: String? - _9 = parseString(reader) - var _10: String? - if Int(_1!) & Int(1 << 4) != 0 {_10 = parseString(reader) } - var _11: Api.DataJSON? - if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() { - _11 = Api.parse(reader, signature: signature) as? Api.DataJSON - } } - var _12: [Api.PaymentFormMethod]? - if Int(_1!) & Int(1 << 6) != 0 {if let _ = reader.readInt32() { - _12 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PaymentFormMethod.self) - } } - var _13: Api.PaymentRequestedInfo? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _13 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo - } } - var _14: [Api.PaymentSavedCredentials]? - if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { - _14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PaymentSavedCredentials.self) - } } - var _15: [Api.User]? + public static func parse_inactiveChats(_ reader: BufferReader) -> InactiveChats? { + var _1: [Int32]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? if let _ = reader.readInt32() { - _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = (Int(_1!) & Int(1 << 5) == 0) || _6 != nil - let _c7 = _7 != nil - let _c8 = _8 != nil - let _c9 = _9 != nil - let _c10 = (Int(_1!) & Int(1 << 4) == 0) || _10 != nil - let _c11 = (Int(_1!) & Int(1 << 4) == 0) || _11 != nil - let _c12 = (Int(_1!) & Int(1 << 6) == 0) || _12 != nil - let _c13 = (Int(_1!) & Int(1 << 0) == 0) || _13 != nil - let _c14 = (Int(_1!) & Int(1 << 1) == 0) || _14 != nil - let _c15 = _15 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 { - return Api.payments.PaymentForm.paymentForm(flags: _1!, formId: _2!, botId: _3!, title: _4!, description: _5!, photo: _6, invoice: _7!, providerId: _8!, url: _9!, nativeProvider: _10, nativeParams: _11, additionalMethods: _12, savedInfo: _13, savedCredentials: _14, users: _15!) + if _c1 && _c2 && _c3 { + return Api.messages.InactiveChats.inactiveChats(dates: _1!, chats: _2!, users: _3!) } else { return nil @@ -864,104 +764,34 @@ public extension Api.payments { } } -public extension Api.payments { - enum PaymentReceipt: TypeConstructorDescription { - case paymentReceipt(flags: Int32, date: Int32, botId: Int64, providerId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, info: Api.PaymentRequestedInfo?, shipping: Api.ShippingOption?, tipAmount: Int64?, currency: String, totalAmount: Int64, credentialsTitle: String, users: [Api.User]) +public extension Api.messages { + enum MessageEditData: TypeConstructorDescription { + case messageEditData(flags: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users): + case .messageEditData(let flags): if boxed { - buffer.appendInt32(1891958275) + buffer.appendInt32(649453030) } serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt64(botId, buffer: buffer, boxed: false) - serializeInt64(providerId, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - serializeString(description, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {photo!.serialize(buffer, true)} - invoice.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {info!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {shipping!.serialize(buffer, true)} - if Int(flags) & Int(1 << 3) != 0 {serializeInt64(tipAmount!, buffer: buffer, boxed: false)} - serializeString(currency, buffer: buffer, boxed: false) - serializeInt64(totalAmount, buffer: buffer, boxed: false) - serializeString(credentialsTitle, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users): - return ("paymentReceipt", [("flags", flags as Any), ("date", date as Any), ("botId", botId as Any), ("providerId", providerId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("info", info as Any), ("shipping", shipping as Any), ("tipAmount", tipAmount as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("credentialsTitle", credentialsTitle as Any), ("users", users as Any)]) + case .messageEditData(let flags): + return ("messageEditData", [("flags", flags as Any)]) } } - public static func parse_paymentReceipt(_ reader: BufferReader) -> PaymentReceipt? { + public static func parse_messageEditData(_ reader: BufferReader) -> MessageEditData? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int64? - _3 = reader.readInt64() - var _4: Int64? - _4 = reader.readInt64() - var _5: String? - _5 = parseString(reader) - var _6: String? - _6 = parseString(reader) - var _7: Api.WebDocument? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.WebDocument - } } - var _8: Api.Invoice? - if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.Invoice - } - var _9: Api.PaymentRequestedInfo? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _9 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo - } } - var _10: Api.ShippingOption? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _10 = Api.parse(reader, signature: signature) as? Api.ShippingOption - } } - var _11: Int64? - if Int(_1!) & Int(1 << 3) != 0 {_11 = reader.readInt64() } - var _12: String? - _12 = parseString(reader) - var _13: Int64? - _13 = reader.readInt64() - var _14: String? - _14 = parseString(reader) - var _15: [Api.User]? - if let _ = reader.readInt32() { - _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil - let _c8 = _8 != nil - let _c9 = (Int(_1!) & Int(1 << 0) == 0) || _9 != nil - let _c10 = (Int(_1!) & Int(1 << 1) == 0) || _10 != nil - let _c11 = (Int(_1!) & Int(1 << 3) == 0) || _11 != nil - let _c12 = _12 != nil - let _c13 = _13 != nil - let _c14 = _14 != nil - let _c15 = _15 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 { - return Api.payments.PaymentReceipt.paymentReceipt(flags: _1!, date: _2!, botId: _3!, providerId: _4!, title: _5!, description: _6!, photo: _7, invoice: _8!, info: _9, shipping: _10, tipAmount: _11, currency: _12!, totalAmount: _13!, credentialsTitle: _14!, users: _15!) + if _c1 { + return Api.messages.MessageEditData.messageEditData(flags: _1!) } else { return nil @@ -970,56 +800,72 @@ public extension Api.payments { } } -public extension Api.payments { - indirect enum PaymentResult: TypeConstructorDescription { - case paymentResult(updates: Api.Updates) - case paymentVerificationNeeded(url: String) +public extension Api.messages { + enum MessageReactionsList: TypeConstructorDescription { + case messageReactionsList(flags: Int32, count: Int32, reactions: [Api.MessagePeerReaction], chats: [Api.Chat], users: [Api.User], nextOffset: String?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .paymentResult(let updates): + case .messageReactionsList(let flags, let count, let reactions, let chats, let users, let nextOffset): if boxed { - buffer.appendInt32(1314881805) + buffer.appendInt32(834488621) } - updates.serialize(buffer, true) - break - case .paymentVerificationNeeded(let url): - if boxed { - buffer.appendInt32(-666824391) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(reactions.count)) + for item in reactions { + item.serialize(buffer, true) } - serializeString(url, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, 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)]) + case .messageReactionsList(let flags, let count, let reactions, let chats, let users, let nextOffset): + return ("messageReactionsList", [("flags", flags as Any), ("count", count as Any), ("reactions", reactions as Any), ("chats", chats as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)]) } } - public static func parse_paymentResult(_ reader: BufferReader) -> PaymentResult? { - var _1: Api.Updates? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Updates + public static func parse_messageReactionsList(_ reader: BufferReader) -> MessageReactionsList? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: [Api.MessagePeerReaction]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessagePeerReaction.self) } - let _c1 = _1 != nil - if _c1 { - return Api.payments.PaymentResult.paymentResult(updates: _1!) + var _4: [Api.Chat]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) } - else { - return nil + var _5: [Api.User]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } - } - public static func parse_paymentVerificationNeeded(_ reader: BufferReader) -> PaymentResult? { - var _1: String? - _1 = parseString(reader) + var _6: String? + if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) } let _c1 = _1 != nil - if _c1 { - return Api.payments.PaymentResult.paymentVerificationNeeded(url: _1!) + 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.messages.MessageReactionsList.messageReactionsList(flags: _1!, count: _2!, reactions: _3!, chats: _4!, users: _5!, nextOffset: _6) } else { return nil @@ -1028,40 +874,60 @@ public extension Api.payments { } } -public extension Api.payments { - enum SavedInfo: TypeConstructorDescription { - case savedInfo(flags: Int32, savedInfo: Api.PaymentRequestedInfo?) +public extension Api.messages { + enum MessageViews: TypeConstructorDescription { + case messageViews(views: [Api.MessageViews], chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .savedInfo(let flags, let savedInfo): + case .messageViews(let views, let chats, let users): if boxed { - buffer.appendInt32(-74456004) + buffer.appendInt32(-1228606141) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(views.count)) + for item in views { + 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) } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {savedInfo!.serialize(buffer, true)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .savedInfo(let flags, let savedInfo): - return ("savedInfo", [("flags", flags as Any), ("savedInfo", savedInfo as Any)]) + case .messageViews(let views, let chats, let users): + return ("messageViews", [("views", views as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_savedInfo(_ reader: BufferReader) -> SavedInfo? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.PaymentRequestedInfo? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo - } } + public static func parse_messageViews(_ reader: BufferReader) -> MessageViews? { + var _1: [Api.MessageViews]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageViews.self) + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.payments.SavedInfo.savedInfo(flags: _1!, savedInfo: _2) + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.messages.MessageViews.messageViews(views: _1!, chats: _2!, users: _3!) } else { return nil @@ -1070,109 +936,83 @@ public extension Api.payments { } } -public extension Api.payments { - enum ValidatedRequestedInfo: TypeConstructorDescription { - case validatedRequestedInfo(flags: Int32, id: String?, shippingOptions: [Api.ShippingOption]?) +public extension Api.messages { + enum Messages: TypeConstructorDescription { + case channelMessages(flags: Int32, pts: Int32, count: Int32, offsetIdOffset: Int32?, messages: [Api.Message], topics: [Api.ForumTopic], chats: [Api.Chat], users: [Api.User]) + case messages(messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + case messagesNotModified(count: Int32) + case messagesSlice(flags: Int32, count: Int32, nextRate: Int32?, offsetIdOffset: Int32?, messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .validatedRequestedInfo(let flags, let id, let shippingOptions): + case .channelMessages(let flags, let pts, let count, let offsetIdOffset, let messages, let topics, let chats, let users): if boxed { - buffer.appendInt32(-784000893) + buffer.appendInt32(-948520370) } serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(id!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(shippingOptions!.count)) - for item in shippingOptions! { + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {serializeInt32(offsetIdOffset!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(topics.count)) + for item in topics { item.serialize(buffer, true) - }} + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .validatedRequestedInfo(let flags, let id, let shippingOptions): - return ("validatedRequestedInfo", [("flags", flags as Any), ("id", id as Any), ("shippingOptions", shippingOptions as Any)]) - } - } - - public static func parse_validatedRequestedInfo(_ reader: BufferReader) -> ValidatedRequestedInfo? { - var _1: Int32? - _1 = reader.readInt32() - var _2: String? - if Int(_1!) & Int(1 << 0) != 0 {_2 = parseString(reader) } - var _3: [Api.ShippingOption]? - if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ShippingOption.self) - } } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.payments.ValidatedRequestedInfo.validatedRequestedInfo(flags: _1!, id: _2, shippingOptions: _3) - } - else { - return nil - } - } - - } -} -public extension Api.phone { - enum ExportedGroupCallInvite: TypeConstructorDescription { - case exportedGroupCallInvite(link: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .exportedGroupCallInvite(let link): + case .messages(let messages, let chats, let users): if boxed { - buffer.appendInt32(541839704) + buffer.appendInt32(-1938715001) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) } - serializeString(link, buffer: buffer, boxed: false) break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .exportedGroupCallInvite(let link): - return ("exportedGroupCallInvite", [("link", link as Any)]) - } - } - - public static func parse_exportedGroupCallInvite(_ reader: BufferReader) -> ExportedGroupCallInvite? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.phone.ExportedGroupCallInvite.exportedGroupCallInvite(link: _1!) - } - else { - return nil - } - } - - } -} -public extension Api.phone { - enum GroupCall: TypeConstructorDescription { - case groupCall(call: Api.GroupCall, participants: [Api.GroupCallParticipant], participantsNextOffset: String, chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .groupCall(let call, let participants, let participantsNextOffset, let chats, let users): + case .messagesNotModified(let count): + if boxed { + buffer.appendInt32(1951620897) + } + serializeInt32(count, buffer: buffer, boxed: false) + break + case .messagesSlice(let flags, let count, let nextRate, let offsetIdOffset, let messages, let chats, let users): if boxed { - buffer.appendInt32(-1636664659) + buffer.appendInt32(978610270) } - call.serialize(buffer, true) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(nextRate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeInt32(offsetIdOffset!, buffer: buffer, boxed: false)} buffer.appendInt32(481674261) - buffer.appendInt32(Int32(participants.count)) - for item in participants { + buffer.appendInt32(Int32(messages.count)) + for item in messages { item.serialize(buffer, true) } - serializeString(participantsNextOffset, buffer: buffer, boxed: false) buffer.appendInt32(481674261) buffer.appendInt32(Int32(chats.count)) for item in chats { @@ -1189,119 +1029,121 @@ public extension Api.phone { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .groupCall(let call, let participants, let participantsNextOffset, let chats, let users): - return ("groupCall", [("call", call as Any), ("participants", participants as Any), ("participantsNextOffset", participantsNextOffset as Any), ("chats", chats as Any), ("users", users as Any)]) + case .channelMessages(let flags, let pts, let count, let offsetIdOffset, let messages, let topics, let chats, let users): + return ("channelMessages", [("flags", flags as Any), ("pts", pts as Any), ("count", count as Any), ("offsetIdOffset", offsetIdOffset as Any), ("messages", messages as Any), ("topics", topics as Any), ("chats", chats as Any), ("users", users as Any)]) + case .messages(let messages, let chats, let users): + return ("messages", [("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + case .messagesNotModified(let count): + return ("messagesNotModified", [("count", count as Any)]) + case .messagesSlice(let flags, let count, let nextRate, let offsetIdOffset, let messages, let chats, let users): + return ("messagesSlice", [("flags", flags as Any), ("count", count as Any), ("nextRate", nextRate as Any), ("offsetIdOffset", offsetIdOffset as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_groupCall(_ reader: BufferReader) -> GroupCall? { - var _1: Api.GroupCall? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.GroupCall + public static func parse_channelMessages(_ reader: BufferReader) -> Messages? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + if Int(_1!) & Int(1 << 2) != 0 {_4 = reader.readInt32() } + var _5: [Api.Message]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) } - var _2: [Api.GroupCallParticipant]? + var _6: [Api.ForumTopic]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.GroupCallParticipant.self) + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ForumTopic.self) } - var _3: String? - _3 = parseString(reader) - var _4: [Api.Chat]? + var _7: [Api.Chat]? if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) } - var _5: [Api.User]? + var _8: [Api.User]? if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - let _c4 = _4 != nil + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.phone.GroupCall.groupCall(call: _1!, participants: _2!, participantsNextOffset: _3!, chats: _4!, users: _5!) + let _c6 = _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { + return Api.messages.Messages.channelMessages(flags: _1!, pts: _2!, count: _3!, offsetIdOffset: _4, messages: _5!, topics: _6!, chats: _7!, users: _8!) } else { return nil } } - - } -} -public extension Api.phone { - enum GroupCallStreamChannels: TypeConstructorDescription { - case groupCallStreamChannels(channels: [Api.GroupCallStreamChannel]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .groupCallStreamChannels(let channels): - if boxed { - buffer.appendInt32(-790330702) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(channels.count)) - for item in channels { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .groupCallStreamChannels(let channels): - return ("groupCallStreamChannels", [("channels", channels as Any)]) - } - } - - public static func parse_groupCallStreamChannels(_ reader: BufferReader) -> GroupCallStreamChannels? { - var _1: [Api.GroupCallStreamChannel]? + public static func parse_messages(_ reader: BufferReader) -> Messages? { + var _1: [Api.Message]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.GroupCallStreamChannel.self) + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.messages.Messages.messages(messages: _1!, chats: _2!, users: _3!) } + else { + return nil + } + } + public static func parse_messagesNotModified(_ reader: BufferReader) -> Messages? { + var _1: Int32? + _1 = reader.readInt32() let _c1 = _1 != nil if _c1 { - return Api.phone.GroupCallStreamChannels.groupCallStreamChannels(channels: _1!) + return Api.messages.Messages.messagesNotModified(count: _1!) } else { return nil } } - - } -} -public extension Api.phone { - enum GroupCallStreamRtmpUrl: TypeConstructorDescription { - case groupCallStreamRtmpUrl(url: String, key: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .groupCallStreamRtmpUrl(let url, let key): - if boxed { - buffer.appendInt32(767505458) - } - serializeString(url, buffer: buffer, boxed: false) - serializeString(key, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .groupCallStreamRtmpUrl(let url, let key): - return ("groupCallStreamRtmpUrl", [("url", url as Any), ("key", key as Any)]) - } - } - - public static func parse_groupCallStreamRtmpUrl(_ reader: BufferReader) -> GroupCallStreamRtmpUrl? { - var _1: String? - _1 = parseString(reader) - var _2: String? - _2 = parseString(reader) + public static func parse_messagesSlice(_ reader: BufferReader) -> Messages? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } + var _4: Int32? + if Int(_1!) & Int(1 << 2) != 0 {_4 = reader.readInt32() } + var _5: [Api.Message]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _6: [Api.Chat]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _7: [Api.User]? + if let _ = reader.readInt32() { + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.phone.GroupCallStreamRtmpUrl.groupCallStreamRtmpUrl(url: _1!, key: _2!) + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.messages.Messages.messagesSlice(flags: _1!, count: _2!, nextRate: _3, offsetIdOffset: _4, messages: _5!, chats: _6!, users: _7!) } else { return nil @@ -1310,23 +1152,26 @@ public extension Api.phone { } } -public extension Api.phone { - enum GroupParticipants: TypeConstructorDescription { - case groupParticipants(count: Int32, participants: [Api.GroupCallParticipant], nextOffset: String, chats: [Api.Chat], users: [Api.User], version: Int32) +public extension Api.messages { + enum PeerDialogs: TypeConstructorDescription { + case peerDialogs(dialogs: [Api.Dialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User], state: Api.updates.State) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .groupParticipants(let count, let participants, let nextOffset, let chats, let users, let version): + case .peerDialogs(let dialogs, let messages, let chats, let users, let state): if boxed { - buffer.appendInt32(-193506890) + buffer.appendInt32(863093588) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(dialogs.count)) + for item in dialogs { + item.serialize(buffer, true) } - serializeInt32(count, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(participants.count)) - for item in participants { + buffer.appendInt32(Int32(messages.count)) + for item in messages { item.serialize(buffer, true) } - serializeString(nextOffset, buffer: buffer, boxed: false) buffer.appendInt32(481674261) buffer.appendInt32(Int32(chats.count)) for item in chats { @@ -1337,45 +1182,46 @@ public extension Api.phone { for item in users { item.serialize(buffer, true) } - serializeInt32(version, buffer: buffer, boxed: false) + state.serialize(buffer, true) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .groupParticipants(let count, let participants, let nextOffset, let chats, let users, let version): - return ("groupParticipants", [("count", count as Any), ("participants", participants as Any), ("nextOffset", nextOffset as Any), ("chats", chats as Any), ("users", users as Any), ("version", version as Any)]) + case .peerDialogs(let dialogs, let messages, let chats, let users, let state): + return ("peerDialogs", [("dialogs", dialogs as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any), ("state", state as Any)]) } } - public static func parse_groupParticipants(_ reader: BufferReader) -> GroupParticipants? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.GroupCallParticipant]? + public static func parse_peerDialogs(_ reader: BufferReader) -> PeerDialogs? { + var _1: [Api.Dialog]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.GroupCallParticipant.self) + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Dialog.self) } - var _3: String? - _3 = parseString(reader) - var _4: [Api.Chat]? + var _2: [Api.Message]? if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) } - var _5: [Api.User]? + var _3: [Api.Chat]? if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _4: [Api.User]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + var _5: Api.updates.State? + if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.updates.State } - var _6: Int32? - _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 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.phone.GroupParticipants.groupParticipants(count: _1!, participants: _2!, nextOffset: _3!, chats: _4!, users: _5!, version: _6!) + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.messages.PeerDialogs.peerDialogs(dialogs: _1!, messages: _2!, chats: _3!, users: _4!, state: _5!) } else { return nil @@ -1384,21 +1230,17 @@ public extension Api.phone { } } -public extension Api.phone { - enum JoinAsPeers: TypeConstructorDescription { - case joinAsPeers(peers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) +public extension Api.messages { + enum PeerSettings: TypeConstructorDescription { + case peerSettings(settings: Api.PeerSettings, chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .joinAsPeers(let peers, let chats, let users): + case .peerSettings(let settings, let chats, let users): if boxed { - buffer.appendInt32(-1343921601) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(peers.count)) - for item in peers { - item.serialize(buffer, true) + buffer.appendInt32(1753266509) } + settings.serialize(buffer, true) buffer.appendInt32(481674261) buffer.appendInt32(Int32(chats.count)) for item in chats { @@ -1415,15 +1257,15 @@ public extension Api.phone { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .joinAsPeers(let peers, let chats, let users): - return ("joinAsPeers", [("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)]) + case .peerSettings(let settings, let chats, let users): + return ("peerSettings", [("settings", settings as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_joinAsPeers(_ reader: BufferReader) -> JoinAsPeers? { - var _1: [Api.Peer]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) + public static func parse_peerSettings(_ reader: BufferReader) -> PeerSettings? { + var _1: Api.PeerSettings? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.PeerSettings } var _2: [Api.Chat]? if let _ = reader.readInt32() { @@ -1437,7 +1279,7 @@ public extension Api.phone { let _c2 = _2 != nil let _c3 = _3 != nil if _c1 && _c2 && _c3 { - return Api.phone.JoinAsPeers.joinAsPeers(peers: _1!, chats: _2!, users: _3!) + return Api.messages.PeerSettings.peerSettings(settings: _1!, chats: _2!, users: _3!) } else { return nil @@ -1446,190 +1288,196 @@ public extension Api.phone { } } -public extension Api.phone { - enum PhoneCall: TypeConstructorDescription { - case phoneCall(phoneCall: Api.PhoneCall, users: [Api.User]) +public extension Api.messages { + enum Reactions: TypeConstructorDescription { + case reactions(hash: Int64, reactions: [Api.Reaction]) + case reactionsNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .phoneCall(let phoneCall, let users): + case .reactions(let hash, let reactions): if boxed { - buffer.appendInt32(-326966976) + buffer.appendInt32(-352454890) } - phoneCall.serialize(buffer, true) + serializeInt64(hash, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { + buffer.appendInt32(Int32(reactions.count)) + for item in reactions { item.serialize(buffer, true) } + break + case .reactionsNotModified: + if boxed { + buffer.appendInt32(-1334846497) + } + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .phoneCall(let phoneCall, let users): - return ("phoneCall", [("phoneCall", phoneCall as Any), ("users", users as Any)]) + case .reactions(let hash, let reactions): + return ("reactions", [("hash", hash as Any), ("reactions", reactions as Any)]) + case .reactionsNotModified: + return ("reactionsNotModified", []) } } - public static func parse_phoneCall(_ reader: BufferReader) -> PhoneCall? { - var _1: Api.PhoneCall? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.PhoneCall - } - var _2: [Api.User]? + public static func parse_reactions(_ reader: BufferReader) -> Reactions? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.Reaction]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Reaction.self) } let _c1 = _1 != nil let _c2 = _2 != nil if _c1 && _c2 { - return Api.phone.PhoneCall.phoneCall(phoneCall: _1!, users: _2!) + return Api.messages.Reactions.reactions(hash: _1!, reactions: _2!) } else { return nil } } + public static func parse_reactionsNotModified(_ reader: BufferReader) -> Reactions? { + return Api.messages.Reactions.reactionsNotModified + } } } -public extension Api.photos { - enum Photo: TypeConstructorDescription { - case photo(photo: Api.Photo, users: [Api.User]) +public extension Api.messages { + enum RecentStickers: TypeConstructorDescription { + case recentStickers(hash: Int64, packs: [Api.StickerPack], stickers: [Api.Document], dates: [Int32]) + case recentStickersNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .photo(let photo, let users): + case .recentStickers(let hash, let packs, let stickers, let dates): if boxed { - buffer.appendInt32(539045032) + buffer.appendInt32(-1999405994) } - photo.serialize(buffer, true) + serializeInt64(hash, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { + buffer.appendInt32(Int32(packs.count)) + for item in packs { item.serialize(buffer, true) } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(stickers.count)) + for item in stickers { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(dates.count)) + for item in dates { + serializeInt32(item, buffer: buffer, boxed: false) + } + break + case .recentStickersNotModified: + if boxed { + buffer.appendInt32(186120336) + } + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .photo(let photo, let users): - return ("photo", [("photo", photo as Any), ("users", users as Any)]) + case .recentStickers(let hash, let packs, let stickers, let dates): + return ("recentStickers", [("hash", hash as Any), ("packs", packs as Any), ("stickers", stickers as Any), ("dates", dates as Any)]) + case .recentStickersNotModified: + return ("recentStickersNotModified", []) } } - public static func parse_photo(_ reader: BufferReader) -> Photo? { - var _1: Api.Photo? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Photo + public static func parse_recentStickers(_ reader: BufferReader) -> RecentStickers? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.StickerPack]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self) } - var _2: [Api.User]? + var _3: [Api.Document]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + } + var _4: [Int32]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.photos.Photo.photo(photo: _1!, users: _2!) + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.messages.RecentStickers.recentStickers(hash: _1!, packs: _2!, stickers: _3!, dates: _4!) } else { return nil } } + public static func parse_recentStickersNotModified(_ reader: BufferReader) -> RecentStickers? { + return Api.messages.RecentStickers.recentStickersNotModified + } } } -public extension Api.photos { - enum Photos: TypeConstructorDescription { - case photos(photos: [Api.Photo], users: [Api.User]) - case photosSlice(count: Int32, photos: [Api.Photo], users: [Api.User]) +public extension Api.messages { + enum SavedGifs: TypeConstructorDescription { + case savedGifs(hash: Int64, gifs: [Api.Document]) + case savedGifsNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .photos(let photos, let users): + case .savedGifs(let hash, let gifs): if boxed { - buffer.appendInt32(-1916114267) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(photos.count)) - for item in photos { - item.serialize(buffer, true) + buffer.appendInt32(-2069878259) } + serializeInt64(hash, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { + buffer.appendInt32(Int32(gifs.count)) + for item in gifs { item.serialize(buffer, true) } break - case .photosSlice(let count, let photos, let users): + case .savedGifsNotModified: if boxed { - buffer.appendInt32(352657236) - } - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(photos.count)) - for item in photos { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + buffer.appendInt32(-402498398) } + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .photos(let photos, let users): - return ("photos", [("photos", photos as Any), ("users", users as Any)]) - case .photosSlice(let count, let photos, let users): - return ("photosSlice", [("count", count as Any), ("photos", photos as Any), ("users", users as Any)]) + case .savedGifs(let hash, let gifs): + return ("savedGifs", [("hash", hash as Any), ("gifs", gifs as Any)]) + case .savedGifsNotModified: + return ("savedGifsNotModified", []) } } - public static func parse_photos(_ reader: BufferReader) -> Photos? { - var _1: [Api.Photo]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Photo.self) - } - var _2: [Api.User]? + public static func parse_savedGifs(_ reader: BufferReader) -> SavedGifs? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.Document]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) } let _c1 = _1 != nil let _c2 = _2 != nil if _c1 && _c2 { - return Api.photos.Photos.photos(photos: _1!, users: _2!) + return Api.messages.SavedGifs.savedGifs(hash: _1!, gifs: _2!) } else { return nil } } - public static func parse_photosSlice(_ reader: BufferReader) -> Photos? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.Photo]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Photo.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.photos.Photos.photosSlice(count: _1!, photos: _2!, users: _3!) - } - else { - return nil - } + public static func parse_savedGifsNotModified(_ reader: BufferReader) -> SavedGifs? { + return Api.messages.SavedGifs.savedGifsNotModified } } diff --git a/submodules/TelegramApi/Sources/Api29.swift b/submodules/TelegramApi/Sources/Api29.swift index e4681269b70..40fbe24d593 100644 --- a/submodules/TelegramApi/Sources/Api29.swift +++ b/submodules/TelegramApi/Sources/Api29.swift @@ -1,59 +1,41 @@ -public extension Api.premium { - enum BoostsList: TypeConstructorDescription { - case boostsList(flags: Int32, count: Int32, boosts: [Api.Boost], nextOffset: String?, users: [Api.User]) +public extension Api.messages { + enum SearchCounter: TypeConstructorDescription { + case searchCounter(flags: Int32, filter: Api.MessagesFilter, count: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .boostsList(let flags, let count, let boosts, let nextOffset, let users): + case .searchCounter(let flags, let filter, let count): if boxed { - buffer.appendInt32(-2030542532) + buffer.appendInt32(-398136321) } serializeInt32(flags, buffer: buffer, boxed: false) + filter.serialize(buffer, true) serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(boosts.count)) - for item in boosts { - item.serialize(buffer, true) - } - if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .boostsList(let flags, let count, let boosts, let nextOffset, let users): - return ("boostsList", [("flags", flags as Any), ("count", count as Any), ("boosts", boosts as Any), ("nextOffset", nextOffset as Any), ("users", users as Any)]) + case .searchCounter(let flags, let filter, let count): + return ("searchCounter", [("flags", flags as Any), ("filter", filter as Any), ("count", count as Any)]) } } - public static func parse_boostsList(_ reader: BufferReader) -> BoostsList? { + public static func parse_searchCounter(_ reader: BufferReader) -> SearchCounter? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: [Api.Boost]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Boost.self) - } - var _4: String? - if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) } - var _5: [Api.User]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + var _2: Api.MessagesFilter? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.MessagesFilter } + var _3: Int32? + _3 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.premium.BoostsList.boostsList(flags: _1!, count: _2!, boosts: _3!, nextOffset: _4, users: _5!) + if _c1 && _c2 && _c3 { + return Api.messages.SearchCounter.searchCounter(flags: _1!, filter: _2!, count: _3!) } else { return nil @@ -62,46 +44,53 @@ public extension Api.premium { } } -public extension Api.premium { - enum BoostsStatus: TypeConstructorDescription { - case boostsStatus(flags: Int32, level: Int32, currentLevelBoosts: Int32, boosts: Int32, giftBoosts: Int32?, nextLevelBoosts: Int32?, premiumAudience: Api.StatsPercentValue?, boostUrl: String, prepaidGiveaways: [Api.PrepaidGiveaway]?, myBoostSlots: [Int32]?) +public extension Api.messages { + enum SearchResultsCalendar: TypeConstructorDescription { + case searchResultsCalendar(flags: Int32, count: Int32, minDate: Int32, minMsgId: Int32, offsetIdOffset: Int32?, periods: [Api.SearchResultsCalendarPeriod], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .boostsStatus(let flags, let level, let currentLevelBoosts, let boosts, let giftBoosts, let nextLevelBoosts, let premiumAudience, let boostUrl, let prepaidGiveaways, let myBoostSlots): + case .searchResultsCalendar(let flags, let count, let minDate, let minMsgId, let offsetIdOffset, let periods, let messages, let chats, let users): if boxed { - buffer.appendInt32(1230586490) + buffer.appendInt32(343859772) } serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(level, buffer: buffer, boxed: false) - serializeInt32(currentLevelBoosts, buffer: buffer, boxed: false) - serializeInt32(boosts, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 4) != 0 {serializeInt32(giftBoosts!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(nextLevelBoosts!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {premiumAudience!.serialize(buffer, true)} - serializeString(boostUrl, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(prepaidGiveaways!.count)) - for item in prepaidGiveaways! { + serializeInt32(count, buffer: buffer, boxed: false) + serializeInt32(minDate, buffer: buffer, boxed: false) + serializeInt32(minMsgId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(offsetIdOffset!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(periods.count)) + for item in periods { item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 2) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(myBoostSlots!.count)) - for item in myBoostSlots! { - serializeInt32(item, buffer: buffer, boxed: false) - }} + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .boostsStatus(let flags, let level, let currentLevelBoosts, let boosts, let giftBoosts, let nextLevelBoosts, let premiumAudience, let boostUrl, let prepaidGiveaways, let myBoostSlots): - return ("boostsStatus", [("flags", flags as Any), ("level", level as Any), ("currentLevelBoosts", currentLevelBoosts as Any), ("boosts", boosts as Any), ("giftBoosts", giftBoosts as Any), ("nextLevelBoosts", nextLevelBoosts as Any), ("premiumAudience", premiumAudience as Any), ("boostUrl", boostUrl as Any), ("prepaidGiveaways", prepaidGiveaways as Any), ("myBoostSlots", myBoostSlots as Any)]) + case .searchResultsCalendar(let flags, let count, let minDate, let minMsgId, let offsetIdOffset, let periods, let messages, let chats, let users): + return ("searchResultsCalendar", [("flags", flags as Any), ("count", count as Any), ("minDate", minDate as Any), ("minMsgId", minMsgId as Any), ("offsetIdOffset", offsetIdOffset as Any), ("periods", periods as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_boostsStatus(_ reader: BufferReader) -> BoostsStatus? { + public static func parse_searchResultsCalendar(_ reader: BufferReader) -> SearchResultsCalendar? { var _1: Int32? _1 = reader.readInt32() var _2: Int32? @@ -111,35 +100,34 @@ public extension Api.premium { var _4: Int32? _4 = reader.readInt32() var _5: Int32? - if Int(_1!) & Int(1 << 4) != 0 {_5 = reader.readInt32() } - var _6: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_6 = reader.readInt32() } - var _7: Api.StatsPercentValue? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.StatsPercentValue - } } - var _8: String? - _8 = parseString(reader) - var _9: [Api.PrepaidGiveaway]? - if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() { - _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PrepaidGiveaway.self) - } } - var _10: [Int32]? - if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() { - _10 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } } + if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() } + var _6: [Api.SearchResultsCalendarPeriod]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SearchResultsCalendarPeriod.self) + } + var _7: [Api.Message]? + if let _ = reader.readInt32() { + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _8: [Api.Chat]? + if let _ = reader.readInt32() { + _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _9: [Api.User]? + if let _ = reader.readInt32() { + _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil - let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil + let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil let _c8 = _8 != nil - let _c9 = (Int(_1!) & Int(1 << 3) == 0) || _9 != nil - let _c10 = (Int(_1!) & Int(1 << 2) == 0) || _10 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { - return Api.premium.BoostsStatus.boostsStatus(flags: _1!, level: _2!, currentLevelBoosts: _3!, boosts: _4!, giftBoosts: _5, nextLevelBoosts: _6, premiumAudience: _7, boostUrl: _8!, prepaidGiveaways: _9, myBoostSlots: _10) + let _c9 = _9 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return Api.messages.SearchResultsCalendar.searchResultsCalendar(flags: _1!, count: _2!, minDate: _3!, minMsgId: _4!, offsetIdOffset: _5, periods: _6!, messages: _7!, chats: _8!, users: _9!) } else { return nil @@ -148,29 +136,20 @@ public extension Api.premium { } } -public extension Api.premium { - enum MyBoosts: TypeConstructorDescription { - case myBoosts(myBoosts: [Api.MyBoost], chats: [Api.Chat], users: [Api.User]) +public extension Api.messages { + enum SearchResultsPositions: TypeConstructorDescription { + case searchResultsPositions(count: Int32, positions: [Api.SearchResultsPosition]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .myBoosts(let myBoosts, let chats, let users): + case .searchResultsPositions(let count, let positions): if boxed { - buffer.appendInt32(-1696454430) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(myBoosts.count)) - for item in myBoosts { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) + buffer.appendInt32(1404185519) } + serializeInt32(count, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { + buffer.appendInt32(Int32(positions.count)) + for item in positions { item.serialize(buffer, true) } break @@ -179,29 +158,22 @@ public extension Api.premium { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .myBoosts(let myBoosts, let chats, let users): - return ("myBoosts", [("myBoosts", myBoosts as Any), ("chats", chats as Any), ("users", users as Any)]) + case .searchResultsPositions(let count, let positions): + return ("searchResultsPositions", [("count", count as Any), ("positions", positions as Any)]) } } - public static func parse_myBoosts(_ reader: BufferReader) -> MyBoosts? { - var _1: [Api.MyBoost]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MyBoost.self) - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? + public static func parse_searchResultsPositions(_ reader: BufferReader) -> SearchResultsPositions? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.SearchResultsPosition]? if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SearchResultsPosition.self) } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.premium.MyBoosts.myBoosts(myBoosts: _1!, chats: _2!, users: _3!) + if _c1 && _c2 { + return Api.messages.SearchResultsPositions.searchResultsPositions(count: _1!, positions: _2!) } else { return nil @@ -210,509 +182,451 @@ public extension Api.premium { } } -public extension Api.stats { - enum BroadcastStats: TypeConstructorDescription { - case broadcastStats(period: Api.StatsDateRangeDays, followers: Api.StatsAbsValueAndPrev, viewsPerPost: Api.StatsAbsValueAndPrev, sharesPerPost: 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, recentMessageInteractions: [Api.MessageInteractionCounters]) +public extension Api.messages { + enum SentEncryptedMessage: TypeConstructorDescription { + case sentEncryptedFile(date: Int32, file: Api.EncryptedFile) + case sentEncryptedMessage(date: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .broadcastStats(let period, let followers, let viewsPerPost, let sharesPerPost, let enabledNotifications, let growthGraph, let followersGraph, let muteGraph, let topHoursGraph, let interactionsGraph, let ivInteractionsGraph, let viewsBySourceGraph, let newFollowersBySourceGraph, let languagesGraph, let recentMessageInteractions): + case .sentEncryptedFile(let date, let file): if boxed { - buffer.appendInt32(-1107852396) + buffer.appendInt32(-1802240206) } - period.serialize(buffer, true) - followers.serialize(buffer, true) - viewsPerPost.serialize(buffer, true) - sharesPerPost.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) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(recentMessageInteractions.count)) - for item in recentMessageInteractions { - item.serialize(buffer, true) + serializeInt32(date, buffer: buffer, boxed: false) + file.serialize(buffer, true) + break + case .sentEncryptedMessage(let date): + if boxed { + buffer.appendInt32(1443858741) } + serializeInt32(date, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .broadcastStats(let period, let followers, let viewsPerPost, let sharesPerPost, let enabledNotifications, let growthGraph, let followersGraph, let muteGraph, let topHoursGraph, let interactionsGraph, let ivInteractionsGraph, let viewsBySourceGraph, let newFollowersBySourceGraph, let languagesGraph, let recentMessageInteractions): - return ("broadcastStats", [("period", period as Any), ("followers", followers as Any), ("viewsPerPost", viewsPerPost as Any), ("sharesPerPost", sharesPerPost 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), ("recentMessageInteractions", recentMessageInteractions as Any)]) + case .sentEncryptedFile(let date, let file): + return ("sentEncryptedFile", [("date", date as Any), ("file", file as Any)]) + case .sentEncryptedMessage(let date): + return ("sentEncryptedMessage", [("date", date 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.StatsPercentValue? - if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.StatsPercentValue - } - var _6: Api.StatsGraph? - if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _7: Api.StatsGraph? - if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _8: Api.StatsGraph? + public static func parse_sentEncryptedFile(_ reader: BufferReader) -> SentEncryptedMessage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.EncryptedFile? if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.StatsGraph + _2 = Api.parse(reader, signature: signature) as? Api.EncryptedFile } - var _9: Api.StatsGraph? - if let signature = reader.readInt32() { - _9 = Api.parse(reader, signature: signature) as? Api.StatsGraph + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.messages.SentEncryptedMessage.sentEncryptedFile(date: _1!, file: _2!) } - var _10: Api.StatsGraph? - if let signature = reader.readInt32() { - _10 = Api.parse(reader, signature: signature) as? Api.StatsGraph + else { + return nil } - var _11: Api.StatsGraph? - if let signature = reader.readInt32() { - _11 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + public static func parse_sentEncryptedMessage(_ reader: BufferReader) -> SentEncryptedMessage? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.messages.SentEncryptedMessage.sentEncryptedMessage(date: _1!) } - var _12: Api.StatsGraph? - if let signature = reader.readInt32() { - _12 = Api.parse(reader, signature: signature) as? Api.StatsGraph + else { + return nil } - var _13: Api.StatsGraph? - if let signature = reader.readInt32() { - _13 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + + } +} +public extension Api.messages { + enum SponsoredMessages: TypeConstructorDescription { + case sponsoredMessages(flags: Int32, postsBetween: Int32?, messages: [Api.SponsoredMessage], chats: [Api.Chat], users: [Api.User]) + case sponsoredMessagesEmpty + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .sponsoredMessages(let flags, let postsBetween, let messages, let chats, let users): + if boxed { + buffer.appendInt32(-907141753) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(postsBetween!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + case .sponsoredMessagesEmpty: + if boxed { + buffer.appendInt32(406407439) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .sponsoredMessages(let flags, let postsBetween, let messages, let chats, let users): + return ("sponsoredMessages", [("flags", flags as Any), ("postsBetween", postsBetween as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + case .sponsoredMessagesEmpty: + return ("sponsoredMessagesEmpty", []) + } + } + + public static func parse_sponsoredMessages(_ reader: BufferReader) -> SponsoredMessages? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } + var _3: [Api.SponsoredMessage]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SponsoredMessage.self) } - var _14: Api.StatsGraph? - if let signature = reader.readInt32() { - _14 = Api.parse(reader, signature: signature) as? Api.StatsGraph + var _4: [Api.Chat]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) } - var _15: [Api.MessageInteractionCounters]? + var _5: [Api.User]? if let _ = reader.readInt32() { - _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageInteractionCounters.self) + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil - let _c2 = _2 != 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 - 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 - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 { - return Api.stats.BroadcastStats.broadcastStats(period: _1!, followers: _2!, viewsPerPost: _3!, sharesPerPost: _4!, enabledNotifications: _5!, growthGraph: _6!, followersGraph: _7!, muteGraph: _8!, topHoursGraph: _9!, interactionsGraph: _10!, ivInteractionsGraph: _11!, viewsBySourceGraph: _12!, newFollowersBySourceGraph: _13!, languagesGraph: _14!, recentMessageInteractions: _15!) + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.messages.SponsoredMessages.sponsoredMessages(flags: _1!, postsBetween: _2, messages: _3!, chats: _4!, users: _5!) } else { return nil } } + public static func parse_sponsoredMessagesEmpty(_ reader: BufferReader) -> SponsoredMessages? { + return Api.messages.SponsoredMessages.sponsoredMessagesEmpty + } } } -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]) +public extension Api.messages { + enum StickerSet: TypeConstructorDescription { + case stickerSet(set: Api.StickerSet, packs: [Api.StickerPack], keywords: [Api.StickerKeyword], documents: [Api.Document]) + case stickerSetNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .megagroupStats(let period, let members, let messages, let viewers, let posters, let growthGraph, let membersGraph, let newMembersBySourceGraph, let languagesGraph, let messagesGraph, let actionsGraph, let topHoursGraph, let weekdaysGraph, let topPosters, let topAdmins, let topInviters, let users): + case .stickerSet(let set, let packs, let keywords, let documents): if boxed { - buffer.appendInt32(-276825834) + buffer.appendInt32(1846886166) } - period.serialize(buffer, true) - members.serialize(buffer, true) - messages.serialize(buffer, true) - viewers.serialize(buffer, true) - posters.serialize(buffer, true) - growthGraph.serialize(buffer, true) - membersGraph.serialize(buffer, true) - newMembersBySourceGraph.serialize(buffer, true) - languagesGraph.serialize(buffer, true) - messagesGraph.serialize(buffer, true) - actionsGraph.serialize(buffer, true) - topHoursGraph.serialize(buffer, true) - weekdaysGraph.serialize(buffer, true) + set.serialize(buffer, true) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(topPosters.count)) - for item in topPosters { + buffer.appendInt32(Int32(packs.count)) + for item in packs { item.serialize(buffer, true) } buffer.appendInt32(481674261) - buffer.appendInt32(Int32(topAdmins.count)) - for item in topAdmins { + buffer.appendInt32(Int32(keywords.count)) + for item in keywords { item.serialize(buffer, true) } buffer.appendInt32(481674261) - buffer.appendInt32(Int32(topInviters.count)) - for item in topInviters { + buffer.appendInt32(Int32(documents.count)) + for item in documents { item.serialize(buffer, true) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) + break + case .stickerSetNotModified: + if boxed { + buffer.appendInt32(-738646805) } + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .megagroupStats(let period, let members, let messages, let viewers, let posters, let growthGraph, let membersGraph, let newMembersBySourceGraph, let languagesGraph, let messagesGraph, let actionsGraph, let topHoursGraph, let weekdaysGraph, let topPosters, let topAdmins, let topInviters, let users): - return ("megagroupStats", [("period", period as Any), ("members", members as Any), ("messages", messages as Any), ("viewers", viewers as Any), ("posters", posters as Any), ("growthGraph", growthGraph as Any), ("membersGraph", membersGraph as Any), ("newMembersBySourceGraph", newMembersBySourceGraph as Any), ("languagesGraph", languagesGraph as Any), ("messagesGraph", messagesGraph as Any), ("actionsGraph", actionsGraph as Any), ("topHoursGraph", topHoursGraph as Any), ("weekdaysGraph", weekdaysGraph as Any), ("topPosters", topPosters as Any), ("topAdmins", topAdmins as Any), ("topInviters", topInviters as Any), ("users", users as Any)]) + case .stickerSet(let set, let packs, let keywords, let documents): + return ("stickerSet", [("set", set as Any), ("packs", packs as Any), ("keywords", keywords as Any), ("documents", documents as Any)]) + case .stickerSetNotModified: + return ("stickerSetNotModified", []) } } - public static func parse_megagroupStats(_ reader: BufferReader) -> MegagroupStats? { - 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.StatsGraph? - if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _7: Api.StatsGraph? - if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _8: Api.StatsGraph? - if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _9: Api.StatsGraph? - if let signature = reader.readInt32() { - _9 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - 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? + public static func parse_stickerSet(_ reader: BufferReader) -> StickerSet? { + var _1: Api.StickerSet? if let signature = reader.readInt32() { - _13 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _14: [Api.StatsGroupTopPoster]? - if let _ = reader.readInt32() { - _14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopPoster.self) + _1 = Api.parse(reader, signature: signature) as? Api.StickerSet } - var _15: [Api.StatsGroupTopAdmin]? + var _2: [Api.StickerPack]? if let _ = reader.readInt32() { - _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopAdmin.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self) } - var _16: [Api.StatsGroupTopInviter]? + var _3: [Api.StickerKeyword]? if let _ = reader.readInt32() { - _16 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopInviter.self) + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerKeyword.self) } - var _17: [Api.User]? + var _4: [Api.Document]? if let _ = reader.readInt32() { - _17 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.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 - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 { - return Api.stats.MegagroupStats.megagroupStats(period: _1!, members: _2!, messages: _3!, viewers: _4!, posters: _5!, growthGraph: _6!, membersGraph: _7!, newMembersBySourceGraph: _8!, languagesGraph: _9!, messagesGraph: _10!, actionsGraph: _11!, topHoursGraph: _12!, weekdaysGraph: _13!, topPosters: _14!, topAdmins: _15!, topInviters: _16!, users: _17!) + if _c1 && _c2 && _c3 && _c4 { + return Api.messages.StickerSet.stickerSet(set: _1!, packs: _2!, keywords: _3!, documents: _4!) } else { return nil } } + public static func parse_stickerSetNotModified(_ reader: BufferReader) -> StickerSet? { + return Api.messages.StickerSet.stickerSetNotModified + } } } -public extension Api.stats { - enum MessageStats: TypeConstructorDescription { - case messageStats(viewsGraph: Api.StatsGraph) +public extension Api.messages { + enum StickerSetInstallResult: TypeConstructorDescription { + case stickerSetInstallResultArchive(sets: [Api.StickerSetCovered]) + case stickerSetInstallResultSuccess public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .messageStats(let viewsGraph): + case .stickerSetInstallResultArchive(let sets): + if boxed { + buffer.appendInt32(904138920) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(sets.count)) + for item in sets { + item.serialize(buffer, true) + } + break + case .stickerSetInstallResultSuccess: if boxed { - buffer.appendInt32(-1986399595) + buffer.appendInt32(946083368) } - viewsGraph.serialize(buffer, true) + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .messageStats(let viewsGraph): - return ("messageStats", [("viewsGraph", viewsGraph as Any)]) + case .stickerSetInstallResultArchive(let sets): + return ("stickerSetInstallResultArchive", [("sets", sets as Any)]) + case .stickerSetInstallResultSuccess: + return ("stickerSetInstallResultSuccess", []) } } - public static func parse_messageStats(_ reader: BufferReader) -> MessageStats? { - var _1: Api.StatsGraph? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.StatsGraph + public static func parse_stickerSetInstallResultArchive(_ reader: BufferReader) -> StickerSetInstallResult? { + var _1: [Api.StickerSetCovered]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self) } let _c1 = _1 != nil if _c1 { - return Api.stats.MessageStats.messageStats(viewsGraph: _1!) + return Api.messages.StickerSetInstallResult.stickerSetInstallResultArchive(sets: _1!) } else { return nil } } + public static func parse_stickerSetInstallResultSuccess(_ reader: BufferReader) -> StickerSetInstallResult? { + return Api.messages.StickerSetInstallResult.stickerSetInstallResultSuccess + } } } -public extension Api.stickers { - enum SuggestedShortName: TypeConstructorDescription { - case suggestedShortName(shortName: String) +public extension Api.messages { + enum Stickers: TypeConstructorDescription { + case stickers(hash: Int64, stickers: [Api.Document]) + case stickersNotModified public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .suggestedShortName(let shortName): + case .stickers(let hash, let stickers): if boxed { - buffer.appendInt32(-2046910401) + buffer.appendInt32(816245886) } - serializeString(shortName, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(stickers.count)) + for item in stickers { + item.serialize(buffer, true) + } + break + case .stickersNotModified: + if boxed { + buffer.appendInt32(-244016606) + } + break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .suggestedShortName(let shortName): - return ("suggestedShortName", [("shortName", shortName as Any)]) + case .stickers(let hash, let stickers): + return ("stickers", [("hash", hash as Any), ("stickers", stickers as Any)]) + case .stickersNotModified: + return ("stickersNotModified", []) } } - public static func parse_suggestedShortName(_ reader: BufferReader) -> SuggestedShortName? { - var _1: String? - _1 = parseString(reader) + public static func parse_stickers(_ reader: BufferReader) -> Stickers? { + 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 - if _c1 { - return Api.stickers.SuggestedShortName.suggestedShortName(shortName: _1!) + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.messages.Stickers.stickers(hash: _1!, stickers: _2!) } else { return nil } } + public static func parse_stickersNotModified(_ reader: BufferReader) -> Stickers? { + return Api.messages.Stickers.stickersNotModified + } } } -public extension Api.storage { - enum FileType: TypeConstructorDescription { - case fileGif - case fileJpeg - case fileMov - case fileMp3 - case fileMp4 - case filePartial - case filePdf - case filePng - case fileUnknown - case fileWebp +public extension Api.messages { + enum TranscribedAudio: TypeConstructorDescription { + case transcribedAudio(flags: Int32, transcriptionId: Int64, text: String, trialRemainsNum: Int32?, trialRemainsUntilDate: Int32?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .fileGif: + case .transcribedAudio(let flags, let transcriptionId, let text, let trialRemainsNum, let trialRemainsUntilDate): if boxed { - buffer.appendInt32(-891180321) + buffer.appendInt32(-809903785) } - - break - case .fileJpeg: - if boxed { - buffer.appendInt32(8322574) - } - - break - case .fileMov: - if boxed { - buffer.appendInt32(1258941372) - } - - break - case .fileMp3: - if boxed { - buffer.appendInt32(1384777335) - } - - break - case .fileMp4: - if boxed { - buffer.appendInt32(-1278304028) - } - - break - case .filePartial: - if boxed { - buffer.appendInt32(1086091090) - } - - break - case .filePdf: - if boxed { - buffer.appendInt32(-1373745011) - } - - break - case .filePng: - if boxed { - buffer.appendInt32(172975040) - } - + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(transcriptionId, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(trialRemainsNum!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(trialRemainsUntilDate!, buffer: buffer, boxed: false)} break - case .fileUnknown: + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .transcribedAudio(let flags, let transcriptionId, let text, let trialRemainsNum, let trialRemainsUntilDate): + return ("transcribedAudio", [("flags", flags as Any), ("transcriptionId", transcriptionId as Any), ("text", text as Any), ("trialRemainsNum", trialRemainsNum as Any), ("trialRemainsUntilDate", trialRemainsUntilDate as Any)]) + } + } + + public static func parse_transcribedAudio(_ reader: BufferReader) -> TranscribedAudio? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: String? + _3 = parseString(reader) + var _4: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_4 = reader.readInt32() } + var _5: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.messages.TranscribedAudio.transcribedAudio(flags: _1!, transcriptionId: _2!, text: _3!, trialRemainsNum: _4, trialRemainsUntilDate: _5) + } + else { + return nil + } + } + + } +} +public extension Api.messages { + enum TranslatedText: TypeConstructorDescription { + case translateResult(result: [Api.TextWithEntities]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .translateResult(let result): if boxed { - buffer.appendInt32(-1432995067) + buffer.appendInt32(870003448) } - - break - case .fileWebp: - if boxed { - buffer.appendInt32(276907596) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(result.count)) + for item in result { + item.serialize(buffer, true) } - break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .fileGif: - return ("fileGif", []) - case .fileJpeg: - return ("fileJpeg", []) - case .fileMov: - return ("fileMov", []) - case .fileMp3: - return ("fileMp3", []) - case .fileMp4: - return ("fileMp4", []) - case .filePartial: - return ("filePartial", []) - case .filePdf: - return ("filePdf", []) - case .filePng: - return ("filePng", []) - case .fileUnknown: - return ("fileUnknown", []) - case .fileWebp: - return ("fileWebp", []) - } - } - - public static func parse_fileGif(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.fileGif - } - public static func parse_fileJpeg(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.fileJpeg - } - public static func parse_fileMov(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.fileMov - } - public static func parse_fileMp3(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.fileMp3 - } - public static func parse_fileMp4(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.fileMp4 - } - public static func parse_filePartial(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.filePartial - } - public static func parse_filePdf(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.filePdf - } - public static func parse_filePng(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.filePng - } - public static func parse_fileUnknown(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.fileUnknown - } - public static func parse_fileWebp(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.fileWebp + case .translateResult(let result): + return ("translateResult", [("result", result as Any)]) + } + } + + public static func parse_translateResult(_ reader: BufferReader) -> TranslatedText? { + var _1: [Api.TextWithEntities]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.TextWithEntities.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.messages.TranslatedText.translateResult(result: _1!) + } + else { + return nil + } } } } -public extension Api.stories { - enum AllStories: TypeConstructorDescription { - case allStories(flags: Int32, count: Int32, state: String, peerStories: [Api.PeerStories], chats: [Api.Chat], users: [Api.User], stealthMode: Api.StoriesStealthMode) - case allStoriesNotModified(flags: Int32, state: String, stealthMode: Api.StoriesStealthMode) +public extension Api.messages { + enum VotesList: TypeConstructorDescription { + case votesList(flags: Int32, count: Int32, votes: [Api.MessagePeerVote], chats: [Api.Chat], users: [Api.User], nextOffset: String?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .allStories(let flags, let count, let state, let peerStories, let chats, let users, let stealthMode): + case .votesList(let flags, let count, let votes, let chats, let users, let nextOffset): if boxed { - buffer.appendInt32(1862033025) + buffer.appendInt32(1218005070) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(count, buffer: buffer, boxed: false) - serializeString(state, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(peerStories.count)) - for item in peerStories { + buffer.appendInt32(Int32(votes.count)) + for item in votes { item.serialize(buffer, true) } buffer.appendInt32(481674261) @@ -725,79 +639,45 @@ public extension Api.stories { for item in users { item.serialize(buffer, true) } - stealthMode.serialize(buffer, true) - break - case .allStoriesNotModified(let flags, let state, let stealthMode): - if boxed { - buffer.appendInt32(291044926) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(state, buffer: buffer, boxed: false) - stealthMode.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .allStories(let flags, let count, let state, let peerStories, let chats, let users, let stealthMode): - return ("allStories", [("flags", flags as Any), ("count", count as Any), ("state", state as Any), ("peerStories", peerStories as Any), ("chats", chats as Any), ("users", users as Any), ("stealthMode", stealthMode as Any)]) - case .allStoriesNotModified(let flags, let state, let stealthMode): - return ("allStoriesNotModified", [("flags", flags as Any), ("state", state as Any), ("stealthMode", stealthMode as Any)]) + case .votesList(let flags, let count, let votes, let chats, let users, let nextOffset): + return ("votesList", [("flags", flags as Any), ("count", count as Any), ("votes", votes as Any), ("chats", chats as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)]) } } - public static func parse_allStories(_ reader: BufferReader) -> AllStories? { + public static func parse_votesList(_ reader: BufferReader) -> VotesList? { var _1: Int32? _1 = reader.readInt32() var _2: Int32? _2 = reader.readInt32() - var _3: String? - _3 = parseString(reader) - var _4: [Api.PeerStories]? + var _3: [Api.MessagePeerVote]? if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PeerStories.self) + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessagePeerVote.self) } - var _5: [Api.Chat]? + var _4: [Api.Chat]? if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) } - var _6: [Api.User]? + var _5: [Api.User]? if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - var _7: Api.StoriesStealthMode? - if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.StoriesStealthMode + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } + var _6: String? + if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.stories.AllStories.allStories(flags: _1!, count: _2!, state: _3!, peerStories: _4!, chats: _5!, users: _6!, stealthMode: _7!) - } - else { - return nil - } - } - public static func parse_allStoriesNotModified(_ reader: BufferReader) -> AllStories? { - var _1: Int32? - _1 = reader.readInt32() - var _2: String? - _2 = parseString(reader) - var _3: Api.StoriesStealthMode? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.StoriesStealthMode - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.stories.AllStories.allStoriesNotModified(flags: _1!, state: _2!, stealthMode: _3!) + let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.messages.VotesList.votesList(flags: _1!, count: _2!, votes: _3!, chats: _4!, users: _5!, nextOffset: _6) } else { return nil @@ -806,17 +686,17 @@ public extension Api.stories { } } -public extension Api.stories { - enum PeerStories: TypeConstructorDescription { - case peerStories(stories: Api.PeerStories, chats: [Api.Chat], users: [Api.User]) +public extension Api.messages { + enum WebPage: TypeConstructorDescription { + case webPage(webpage: Api.WebPage, chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .peerStories(let stories, let chats, let users): + case .webPage(let webpage, let chats, let users): if boxed { - buffer.appendInt32(-890861720) + buffer.appendInt32(-44166467) } - stories.serialize(buffer, true) + webpage.serialize(buffer, true) buffer.appendInt32(481674261) buffer.appendInt32(Int32(chats.count)) for item in chats { @@ -833,15 +713,15 @@ public extension Api.stories { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .peerStories(let stories, let chats, let users): - return ("peerStories", [("stories", stories as Any), ("chats", chats as Any), ("users", users as Any)]) + case .webPage(let webpage, let chats, let users): + return ("webPage", [("webpage", webpage as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_peerStories(_ reader: BufferReader) -> PeerStories? { - var _1: Api.PeerStories? + public static func parse_webPage(_ reader: BufferReader) -> WebPage? { + var _1: Api.WebPage? if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.PeerStories + _1 = Api.parse(reader, signature: signature) as? Api.WebPage } var _2: [Api.Chat]? if let _ = reader.readInt32() { @@ -855,7 +735,7 @@ public extension Api.stories { let _c2 = _2 != nil let _c3 = _3 != nil if _c1 && _c2 && _c3 { - return Api.stories.PeerStories.peerStories(stories: _1!, chats: _2!, users: _3!) + return Api.messages.WebPage.webPage(webpage: _1!, chats: _2!, users: _3!) } else { return nil @@ -864,30 +744,20 @@ public extension Api.stories { } } -public extension Api.stories { - enum Stories: TypeConstructorDescription { - case stories(count: Int32, stories: [Api.StoryItem], chats: [Api.Chat], users: [Api.User]) +public extension Api.payments { + enum BankCardData: TypeConstructorDescription { + case bankCardData(title: String, openUrls: [Api.BankCardOpenUrl]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .stories(let count, let stories, let chats, let users): + case .bankCardData(let title, let openUrls): if boxed { - buffer.appendInt32(1574486984) - } - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(stories.count)) - for item in stories { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) + buffer.appendInt32(1042605427) } + serializeString(title, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { + buffer.appendInt32(Int32(openUrls.count)) + for item in openUrls { item.serialize(buffer, true) } break @@ -896,32 +766,22 @@ public extension Api.stories { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .stories(let count, let stories, let chats, let users): - return ("stories", [("count", count as Any), ("stories", stories as Any), ("chats", chats as Any), ("users", users as Any)]) + case .bankCardData(let title, let openUrls): + return ("bankCardData", [("title", title as Any), ("openUrls", openUrls as Any)]) } } - public static func parse_stories(_ reader: BufferReader) -> Stories? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.StoryItem]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryItem.self) - } - var _3: [Api.Chat]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _4: [Api.User]? + public static func parse_bankCardData(_ reader: BufferReader) -> BankCardData? { + var _1: String? + _1 = parseString(reader) + var _2: [Api.BankCardOpenUrl]? if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.BankCardOpenUrl.self) } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.stories.Stories.stories(count: _1!, stories: _2!, chats: _3!, users: _4!) + if _c1 && _c2 { + return Api.payments.BankCardData.bankCardData(title: _1!, openUrls: _2!) } else { return nil @@ -930,19 +790,26 @@ public extension Api.stories { } } -public extension Api.stories { - enum StoryViews: TypeConstructorDescription { - case storyViews(views: [Api.StoryViews], users: [Api.User]) +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]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .storyViews(let views, let users): + case .checkedGiftCode(let flags, let fromId, let giveawayMsgId, let toId, let date, let months, let usedDate, let chats, let users): if boxed { - buffer.appendInt32(-560009955) + buffer.appendInt32(-1222446760) } + serializeInt32(flags, buffer: buffer, boxed: false) + fromId.serialize(buffer, true) + if Int(flags) & Int(1 << 3) != 0 {serializeInt32(giveawayMsgId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeInt64(toId!, buffer: buffer, boxed: false)} + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt32(months, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(usedDate!, buffer: buffer, boxed: false)} buffer.appendInt32(481674261) - buffer.appendInt32(Int32(views.count)) - for item in views { + buffer.appendInt32(Int32(chats.count)) + for item in chats { item.serialize(buffer, true) } buffer.appendInt32(481674261) @@ -956,24 +823,47 @@ public extension Api.stories { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .storyViews(let views, let users): - return ("storyViews", [("views", views as Any), ("users", users as Any)]) + case .checkedGiftCode(let flags, let fromId, let giveawayMsgId, let toId, let date, let months, let usedDate, let chats, let users): + return ("checkedGiftCode", [("flags", flags as Any), ("fromId", fromId as Any), ("giveawayMsgId", giveawayMsgId as Any), ("toId", toId as Any), ("date", date as Any), ("months", months as Any), ("usedDate", usedDate as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_storyViews(_ reader: BufferReader) -> StoryViews? { - var _1: [Api.StoryViews]? + public static func parse_checkedGiftCode(_ reader: BufferReader) -> CheckedGiftCode? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Peer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _3: Int32? + if Int(_1!) & Int(1 << 3) != 0 {_3 = reader.readInt32() } + var _4: Int64? + if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt64() } + var _5: Int32? + _5 = reader.readInt32() + var _6: Int32? + _6 = reader.readInt32() + var _7: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_7 = reader.readInt32() } + var _8: [Api.Chat]? if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryViews.self) + _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) } - var _2: [Api.User]? + var _9: [Api.User]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.stories.StoryViews.storyViews(views: _1!, users: _2!) + let _c3 = (Int(_1!) & Int(1 << 3) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil + let _c8 = _8 != nil + let _c9 = _9 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return Api.payments.CheckedGiftCode.checkedGiftCode(flags: _1!, fromId: _2!, giveawayMsgId: _3, toId: _4, date: _5!, months: _6!, usedDate: _7, chats: _8!, users: _9!) } else { return nil @@ -982,66 +872,34 @@ public extension Api.stories { } } -public extension Api.stories { - enum StoryViewsList: TypeConstructorDescription { - case storyViewsList(flags: Int32, count: Int32, reactionsCount: Int32, views: [Api.StoryView], users: [Api.User], nextOffset: String?) +public extension Api.payments { + enum ExportedInvoice: TypeConstructorDescription { + case exportedInvoice(url: String) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .storyViewsList(let flags, let count, let reactionsCount, let views, let users, let nextOffset): + case .exportedInvoice(let url): if boxed { - buffer.appendInt32(1189722604) + buffer.appendInt32(-1362048039) } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(count, buffer: buffer, boxed: false) - serializeInt32(reactionsCount, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(views.count)) - for item in views { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} + serializeString(url, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .storyViewsList(let flags, let count, let reactionsCount, let views, let users, let nextOffset): - return ("storyViewsList", [("flags", flags as Any), ("count", count as Any), ("reactionsCount", reactionsCount as Any), ("views", views as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)]) + case .exportedInvoice(let url): + return ("exportedInvoice", [("url", url as Any)]) } } - public static func parse_storyViewsList(_ reader: BufferReader) -> StoryViewsList? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() - var _4: [Api.StoryView]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryView.self) - } - var _5: [Api.User]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - var _6: String? - if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) } + public static func parse_exportedInvoice(_ reader: BufferReader) -> ExportedInvoice? { + var _1: String? + _1 = parseString(reader) let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.stories.StoryViewsList.storyViewsList(flags: _1!, count: _2!, reactionsCount: _3!, views: _4!, users: _5!, nextOffset: _6) + if _c1 { + return Api.payments.ExportedInvoice.exportedInvoice(url: _1!) } else { return nil @@ -1050,170 +908,90 @@ public extension Api.stories { } } -public extension Api.updates { - indirect enum ChannelDifference: TypeConstructorDescription { - case channelDifference(flags: Int32, pts: Int32, timeout: Int32?, newMessages: [Api.Message], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User]) - case channelDifferenceEmpty(flags: Int32, pts: Int32, timeout: Int32?) - case channelDifferenceTooLong(flags: Int32, timeout: Int32?, dialog: Api.Dialog, messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) +public extension Api.payments { + enum GiveawayInfo: TypeConstructorDescription { + case giveawayInfo(flags: Int32, startDate: Int32, joinedTooEarlyDate: Int32?, adminDisallowedChatId: Int64?, disallowedCountry: String?) + case giveawayInfoResults(flags: Int32, startDate: Int32, giftCodeSlug: String?, finishDate: Int32, winnersCount: Int32, activatedCount: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .channelDifference(let flags, let pts, let timeout, let newMessages, let otherUpdates, let chats, let users): - if boxed { - buffer.appendInt32(543450958) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(pts, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(newMessages.count)) - for item in newMessages { - 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) - } - break - case .channelDifferenceEmpty(let flags, let pts, let timeout): + case .giveawayInfo(let flags, let startDate, let joinedTooEarlyDate, let adminDisallowedChatId, let disallowedCountry): if boxed { - buffer.appendInt32(1041346555) + buffer.appendInt32(1130879648) } serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(pts, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)} + serializeInt32(startDate, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(joinedTooEarlyDate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeInt64(adminDisallowedChatId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {serializeString(disallowedCountry!, buffer: buffer, boxed: false)} break - case .channelDifferenceTooLong(let flags, let timeout, let dialog, let messages, let chats, let users): + case .giveawayInfoResults(let flags, let startDate, let giftCodeSlug, let finishDate, let winnersCount, let activatedCount): if boxed { - buffer.appendInt32(-1531132162) + buffer.appendInt32(13456752) } serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)} - dialog.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } + serializeInt32(startDate, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(giftCodeSlug!, buffer: buffer, boxed: false)} + serializeInt32(finishDate, buffer: buffer, boxed: false) + serializeInt32(winnersCount, buffer: buffer, boxed: false) + serializeInt32(activatedCount, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .channelDifference(let flags, let pts, let timeout, let newMessages, let otherUpdates, let chats, let users): - return ("channelDifference", [("flags", flags as Any), ("pts", pts as Any), ("timeout", timeout as Any), ("newMessages", newMessages as Any), ("otherUpdates", otherUpdates as Any), ("chats", chats as Any), ("users", users as Any)]) - case .channelDifferenceEmpty(let flags, let pts, let timeout): - return ("channelDifferenceEmpty", [("flags", flags as Any), ("pts", pts as Any), ("timeout", timeout as Any)]) - case .channelDifferenceTooLong(let flags, let timeout, let dialog, let messages, let chats, let users): - return ("channelDifferenceTooLong", [("flags", flags as Any), ("timeout", timeout as Any), ("dialog", dialog as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + case .giveawayInfo(let flags, let startDate, let joinedTooEarlyDate, let adminDisallowedChatId, let disallowedCountry): + return ("giveawayInfo", [("flags", flags as Any), ("startDate", startDate as Any), ("joinedTooEarlyDate", joinedTooEarlyDate as Any), ("adminDisallowedChatId", adminDisallowedChatId as Any), ("disallowedCountry", disallowedCountry as Any)]) + case .giveawayInfoResults(let flags, let startDate, let giftCodeSlug, let finishDate, let winnersCount, let activatedCount): + return ("giveawayInfoResults", [("flags", flags as Any), ("startDate", startDate as Any), ("giftCodeSlug", giftCodeSlug as Any), ("finishDate", finishDate as Any), ("winnersCount", winnersCount as Any), ("activatedCount", activatedCount as Any)]) } } - public static func parse_channelDifference(_ reader: BufferReader) -> ChannelDifference? { + public static func parse_giveawayInfo(_ reader: BufferReader) -> GiveawayInfo? { var _1: Int32? _1 = reader.readInt32() var _2: Int32? _2 = reader.readInt32() var _3: Int32? if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() } - var _4: [Api.Message]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _5: [Api.Update]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Update.self) - } - var _6: [Api.Chat]? - if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _7: [Api.User]? - if let _ = reader.readInt32() { - _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } + var _4: Int64? + if Int(_1!) & Int(1 << 2) != 0 {_4 = reader.readInt64() } + var _5: String? + if Int(_1!) & Int(1 << 4) != 0 {_5 = parseString(reader) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _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.updates.ChannelDifference.channelDifference(flags: _1!, pts: _2!, timeout: _3, newMessages: _4!, otherUpdates: _5!, chats: _6!, users: _7!) + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.payments.GiveawayInfo.giveawayInfo(flags: _1!, startDate: _2!, joinedTooEarlyDate: _3, adminDisallowedChatId: _4, disallowedCountry: _5) } else { return nil } } - public static func parse_channelDifferenceEmpty(_ reader: BufferReader) -> ChannelDifference? { + public static func parse_giveawayInfoResults(_ reader: BufferReader) -> GiveawayInfo? { var _1: Int32? _1 = reader.readInt32() var _2: Int32? _2 = reader.readInt32() - var _3: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() } + var _3: String? + if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() + var _6: Int32? + _6 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.updates.ChannelDifference.channelDifferenceEmpty(flags: _1!, pts: _2!, timeout: _3) - } - else { - return nil - } - } - public static func parse_channelDifferenceTooLong(_ reader: BufferReader) -> ChannelDifference? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() } - var _3: Api.Dialog? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.Dialog - } - var _4: [Api.Message]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _5: [Api.Chat]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _6: [Api.User]? - if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil - let _c3 = _3 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.updates.ChannelDifference.channelDifferenceTooLong(flags: _1!, timeout: _2, dialog: _3!, messages: _4!, chats: _5!, users: _6!) + return Api.payments.GiveawayInfo.giveawayInfoResults(flags: _1!, startDate: _2!, giftCodeSlug: _3, finishDate: _4!, winnersCount: _5!, activatedCount: _6!) } else { return nil @@ -1222,130 +1000,204 @@ 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 extension Api.payments { + enum PaymentForm: TypeConstructorDescription { + case paymentForm(flags: Int32, formId: Int64, botId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, providerId: Int64, url: String, nativeProvider: String?, nativeParams: Api.DataJSON?, additionalMethods: [Api.PaymentFormMethod]?, savedInfo: Api.PaymentRequestedInfo?, savedCredentials: [Api.PaymentSavedCredentials]?, users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .difference(let newMessages, let newEncryptedMessages, let otherUpdates, let chats, let users, let state): + case .paymentForm(let flags, let formId, let botId, let title, let description, let photo, let invoice, let providerId, let url, let nativeProvider, let nativeParams, let additionalMethods, let savedInfo, let savedCredentials, let users): 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(-1610250415) } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(otherUpdates.count)) - for item in otherUpdates { + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(formId, buffer: buffer, boxed: false) + serializeInt64(botId, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + serializeString(description, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 5) != 0 {photo!.serialize(buffer, true)} + invoice.serialize(buffer, true) + serializeInt64(providerId, buffer: buffer, boxed: false) + serializeString(url, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 4) != 0 {serializeString(nativeProvider!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {nativeParams!.serialize(buffer, true)} + if Int(flags) & Int(1 << 6) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(additionalMethods!.count)) + for item in additionalMethods! { item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { + }} + if Int(flags) & Int(1 << 0) != 0 {savedInfo!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(savedCredentials!.count)) + for item in savedCredentials! { 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): + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .paymentForm(let flags, let formId, let botId, let title, let description, let photo, let invoice, let providerId, let url, let nativeProvider, let nativeParams, let additionalMethods, let savedInfo, let savedCredentials, let users): + return ("paymentForm", [("flags", flags as Any), ("formId", formId as Any), ("botId", botId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("providerId", providerId as Any), ("url", url as Any), ("nativeProvider", nativeProvider as Any), ("nativeParams", nativeParams as Any), ("additionalMethods", additionalMethods as Any), ("savedInfo", savedInfo as Any), ("savedCredentials", savedCredentials as Any), ("users", users as Any)]) + } + } + + public static func parse_paymentForm(_ reader: BufferReader) -> PaymentForm? { + 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.WebDocument? + if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.WebDocument + } } + var _7: Api.Invoice? + if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.Invoice + } + var _8: Int64? + _8 = reader.readInt64() + var _9: String? + _9 = parseString(reader) + var _10: String? + if Int(_1!) & Int(1 << 4) != 0 {_10 = parseString(reader) } + var _11: Api.DataJSON? + if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() { + _11 = Api.parse(reader, signature: signature) as? Api.DataJSON + } } + var _12: [Api.PaymentFormMethod]? + if Int(_1!) & Int(1 << 6) != 0 {if let _ = reader.readInt32() { + _12 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PaymentFormMethod.self) + } } + var _13: Api.PaymentRequestedInfo? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _13 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo + } } + var _14: [Api.PaymentSavedCredentials]? + if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { + _14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PaymentSavedCredentials.self) + } } + var _15: [Api.User]? + if let _ = reader.readInt32() { + _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = (Int(_1!) & Int(1 << 5) == 0) || _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + let _c9 = _9 != nil + let _c10 = (Int(_1!) & Int(1 << 4) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 4) == 0) || _11 != nil + let _c12 = (Int(_1!) & Int(1 << 6) == 0) || _12 != nil + let _c13 = (Int(_1!) & Int(1 << 0) == 0) || _13 != nil + let _c14 = (Int(_1!) & Int(1 << 1) == 0) || _14 != nil + let _c15 = _15 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 { + return Api.payments.PaymentForm.paymentForm(flags: _1!, formId: _2!, botId: _3!, title: _4!, description: _5!, photo: _6, invoice: _7!, providerId: _8!, url: _9!, nativeProvider: _10, nativeParams: _11, additionalMethods: _12, savedInfo: _13, savedCredentials: _14, users: _15!) + } + else { + return nil + } + } + + } +} +public extension Api.payments { + enum PaymentReceipt: TypeConstructorDescription { + case paymentReceipt(flags: Int32, date: Int32, botId: Int64, providerId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, info: Api.PaymentRequestedInfo?, shipping: Api.ShippingOption?, tipAmount: Int64?, currency: String, totalAmount: Int64, credentialsTitle: String, users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users): if boxed { - buffer.appendInt32(1567990072) + buffer.appendInt32(1891958275) } + serializeInt32(flags, buffer: buffer, boxed: false) 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) - } + serializeInt64(botId, buffer: buffer, boxed: false) + serializeInt64(providerId, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + serializeString(description, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {photo!.serialize(buffer, true)} + invoice.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {info!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {shipping!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {serializeInt64(tipAmount!, buffer: buffer, boxed: false)} + serializeString(currency, buffer: buffer, boxed: false) + serializeInt64(totalAmount, buffer: buffer, boxed: false) + serializeString(credentialsTitle, buffer: buffer, boxed: false) buffer.appendInt32(481674261) buffer.appendInt32(Int32(users.count)) for item in users { item.serialize(buffer, true) } - 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)]) + case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users): + return ("paymentReceipt", [("flags", flags as Any), ("date", date as Any), ("botId", botId as Any), ("providerId", providerId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("info", info as Any), ("shipping", shipping as Any), ("tipAmount", tipAmount as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("credentialsTitle", credentialsTitle as Any), ("users", users as Any)]) } } - public static func parse_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) + public static func parse_paymentReceipt(_ reader: BufferReader) -> PaymentReceipt? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int64? + _3 = reader.readInt64() + var _4: Int64? + _4 = reader.readInt64() + var _5: String? + _5 = parseString(reader) + var _6: String? + _6 = parseString(reader) + var _7: Api.WebDocument? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.WebDocument + } } + var _8: Api.Invoice? + if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.Invoice } - var _5: [Api.User]? + var _9: Api.PaymentRequestedInfo? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo + } } + var _10: Api.ShippingOption? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _10 = Api.parse(reader, signature: signature) as? Api.ShippingOption + } } + var _11: Int64? + if Int(_1!) & Int(1 << 3) != 0 {_11 = reader.readInt64() } + var _12: String? + _12 = parseString(reader) + var _13: Int64? + _13 = reader.readInt64() + var _14: String? + _14 = parseString(reader) + var _15: [Api.User]? if let _ = reader.readInt32() { - _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 + _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil @@ -1353,71 +1205,203 @@ public extension Api.updates { 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!) + let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil + let _c8 = _8 != nil + let _c9 = (Int(_1!) & Int(1 << 0) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 1) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 3) == 0) || _11 != nil + let _c12 = _12 != nil + let _c13 = _13 != nil + let _c14 = _14 != nil + let _c15 = _15 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 { + return Api.payments.PaymentReceipt.paymentReceipt(flags: _1!, date: _2!, botId: _3!, providerId: _4!, title: _5!, description: _6!, photo: _7, invoice: _8!, info: _9, shipping: _10, tipAmount: _11, currency: _12!, totalAmount: _13!, credentialsTitle: _14!, users: _15!) } else { return nil } } - public static func parse_differenceEmpty(_ reader: BufferReader) -> Difference? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() + + } +} +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 - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.updates.Difference.differenceEmpty(date: _1!, seq: _2!) + if _c1 { + return Api.payments.PaymentResult.paymentResult(updates: _1!) } 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) + 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!) } - var _6: Api.updates.State? - if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.updates.State + else { + return nil } + } + + } +} +public extension Api.payments { + enum SavedInfo: TypeConstructorDescription { + case savedInfo(flags: Int32, savedInfo: Api.PaymentRequestedInfo?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .savedInfo(let flags, let savedInfo): + if boxed { + buffer.appendInt32(-74456004) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {savedInfo!.serialize(buffer, true)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .savedInfo(let flags, let savedInfo): + return ("savedInfo", [("flags", flags as Any), ("savedInfo", savedInfo as Any)]) + } + } + + public static func parse_savedInfo(_ reader: BufferReader) -> SavedInfo? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.PaymentRequestedInfo? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo + } } let _c1 = _1 != nil - let _c2 = _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!) + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + if _c1 && _c2 { + return Api.payments.SavedInfo.savedInfo(flags: _1!, savedInfo: _2) } else { return nil } } - public static func parse_differenceTooLong(_ reader: BufferReader) -> Difference? { + + } +} +public extension Api.payments { + enum ValidatedRequestedInfo: TypeConstructorDescription { + case validatedRequestedInfo(flags: Int32, id: String?, shippingOptions: [Api.ShippingOption]?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .validatedRequestedInfo(let flags, let id, let shippingOptions): + if boxed { + buffer.appendInt32(-784000893) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(id!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(shippingOptions!.count)) + for item in shippingOptions! { + item.serialize(buffer, true) + }} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .validatedRequestedInfo(let flags, let id, let shippingOptions): + return ("validatedRequestedInfo", [("flags", flags as Any), ("id", id as Any), ("shippingOptions", shippingOptions as Any)]) + } + } + + public static func parse_validatedRequestedInfo(_ reader: BufferReader) -> ValidatedRequestedInfo? { var _1: Int32? _1 = reader.readInt32() + var _2: String? + if Int(_1!) & Int(1 << 0) != 0 {_2 = parseString(reader) } + var _3: [Api.ShippingOption]? + if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ShippingOption.self) + } } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.payments.ValidatedRequestedInfo.validatedRequestedInfo(flags: _1!, id: _2, shippingOptions: _3) + } + else { + return nil + } + } + + } +} +public extension Api.phone { + enum ExportedGroupCallInvite: TypeConstructorDescription { + case exportedGroupCallInvite(link: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .exportedGroupCallInvite(let link): + if boxed { + buffer.appendInt32(541839704) + } + serializeString(link, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .exportedGroupCallInvite(let link): + return ("exportedGroupCallInvite", [("link", link as Any)]) + } + } + + public static func parse_exportedGroupCallInvite(_ reader: BufferReader) -> ExportedGroupCallInvite? { + var _1: String? + _1 = parseString(reader) let _c1 = _1 != nil if _c1 { - return Api.updates.Difference.differenceTooLong(pts: _1!) + return Api.phone.ExportedGroupCallInvite.exportedGroupCallInvite(link: _1!) } else { return nil @@ -1426,50 +1410,70 @@ public extension Api.updates { } } -public extension Api.updates { - enum State: TypeConstructorDescription { - case state(pts: Int32, qts: Int32, date: Int32, seq: Int32, unreadCount: Int32) +public extension Api.phone { + enum GroupCall: TypeConstructorDescription { + case groupCall(call: Api.GroupCall, participants: [Api.GroupCallParticipant], participantsNextOffset: String, chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .state(let pts, let qts, let date, let seq, let unreadCount): + case .groupCall(let call, let participants, let participantsNextOffset, let chats, let users): if boxed { - buffer.appendInt32(-1519637954) + buffer.appendInt32(-1636664659) + } + call.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(participants.count)) + for item in participants { + item.serialize(buffer, true) + } + serializeString(participantsNextOffset, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) } - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(qts, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt32(seq, buffer: buffer, boxed: false) - serializeInt32(unreadCount, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .state(let pts, let qts, let date, let seq, let unreadCount): - return ("state", [("pts", pts as Any), ("qts", qts as Any), ("date", date as Any), ("seq", seq as Any), ("unreadCount", unreadCount as Any)]) + case .groupCall(let call, let participants, let participantsNextOffset, let chats, let users): + return ("groupCall", [("call", call as Any), ("participants", participants as Any), ("participantsNextOffset", participantsNextOffset as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_state(_ reader: BufferReader) -> State? { - 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() + public static func parse_groupCall(_ reader: BufferReader) -> GroupCall? { + var _1: Api.GroupCall? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.GroupCall + } + var _2: [Api.GroupCallParticipant]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.GroupCallParticipant.self) + } + var _3: String? + _3 = parseString(reader) + var _4: [Api.Chat]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _5: [Api.User]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.updates.State.state(pts: _1!, qts: _2!, date: _3!, seq: _4!, unreadCount: _5!) + return Api.phone.GroupCall.groupCall(call: _1!, participants: _2!, participantsNextOffset: _3!, chats: _4!, users: _5!) } else { return nil @@ -1478,54 +1482,80 @@ public extension Api.updates { } } -public extension Api.upload { - enum CdnFile: TypeConstructorDescription { - case cdnFile(bytes: Buffer) - case cdnFileReuploadNeeded(requestToken: Buffer) +public extension Api.phone { + enum GroupCallStreamChannels: TypeConstructorDescription { + case groupCallStreamChannels(channels: [Api.GroupCallStreamChannel]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .cdnFile(let bytes): + case .groupCallStreamChannels(let channels): if boxed { - buffer.appendInt32(-1449145777) + buffer.appendInt32(-790330702) } - serializeBytes(bytes, buffer: buffer, boxed: false) - break - case .cdnFileReuploadNeeded(let requestToken): - if boxed { - buffer.appendInt32(-290921362) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(channels.count)) + for item in channels { + item.serialize(buffer, true) } - serializeBytes(requestToken, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .cdnFile(let bytes): - return ("cdnFile", [("bytes", bytes as Any)]) - case .cdnFileReuploadNeeded(let requestToken): - return ("cdnFileReuploadNeeded", [("requestToken", requestToken as Any)]) + case .groupCallStreamChannels(let channels): + return ("groupCallStreamChannels", [("channels", channels as Any)]) } } - public static func parse_cdnFile(_ reader: BufferReader) -> CdnFile? { - var _1: Buffer? - _1 = parseBytes(reader) + public static func parse_groupCallStreamChannels(_ reader: BufferReader) -> GroupCallStreamChannels? { + var _1: [Api.GroupCallStreamChannel]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.GroupCallStreamChannel.self) + } let _c1 = _1 != nil if _c1 { - return Api.upload.CdnFile.cdnFile(bytes: _1!) + return Api.phone.GroupCallStreamChannels.groupCallStreamChannels(channels: _1!) } else { return nil } } - public static func parse_cdnFileReuploadNeeded(_ reader: BufferReader) -> CdnFile? { - var _1: Buffer? - _1 = parseBytes(reader) + + } +} +public extension Api.phone { + enum GroupCallStreamRtmpUrl: TypeConstructorDescription { + case groupCallStreamRtmpUrl(url: String, key: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .groupCallStreamRtmpUrl(let url, let key): + if boxed { + buffer.appendInt32(767505458) + } + serializeString(url, buffer: buffer, boxed: false) + serializeString(key, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .groupCallStreamRtmpUrl(let url, let key): + return ("groupCallStreamRtmpUrl", [("url", url as Any), ("key", key as Any)]) + } + } + + public static func parse_groupCallStreamRtmpUrl(_ reader: BufferReader) -> GroupCallStreamRtmpUrl? { + var _1: String? + _1 = parseString(reader) + var _2: String? + _2 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.upload.CdnFile.cdnFileReuploadNeeded(requestToken: _1!) + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.phone.GroupCallStreamRtmpUrl.groupCallStreamRtmpUrl(url: _1!, key: _2!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index b3d8cbead55..1dbcfb84bdb 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -522,7 +522,7 @@ public extension Api { } public extension Api { indirect enum Chat: TypeConstructorDescription { - case channel(flags: Int32, flags2: Int32, id: Int64, accessHash: Int64?, title: String, username: String?, photo: Api.ChatPhoto, date: Int32, restrictionReason: [Api.RestrictionReason]?, adminRights: Api.ChatAdminRights?, bannedRights: Api.ChatBannedRights?, defaultBannedRights: Api.ChatBannedRights?, participantsCount: Int32?, usernames: [Api.Username]?, storiesMaxId: Int32?, color: Int32?, backgroundEmojiId: Int64?) + case channel(flags: Int32, flags2: Int32, id: Int64, accessHash: Int64?, title: String, username: String?, photo: Api.ChatPhoto, date: Int32, restrictionReason: [Api.RestrictionReason]?, adminRights: Api.ChatAdminRights?, bannedRights: Api.ChatBannedRights?, defaultBannedRights: Api.ChatBannedRights?, participantsCount: Int32?, usernames: [Api.Username]?, storiesMaxId: Int32?, color: Api.PeerColor?) case channelForbidden(flags: Int32, id: Int64, accessHash: Int64, title: String, untilDate: Int32?) case chat(flags: Int32, id: Int64, title: String, photo: Api.ChatPhoto, participantsCount: Int32, date: Int32, version: Int32, migratedTo: Api.InputChannel?, adminRights: Api.ChatAdminRights?, defaultBannedRights: Api.ChatBannedRights?) case chatEmpty(id: Int64) @@ -530,9 +530,9 @@ public extension Api { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .channel(let flags, let flags2, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount, let usernames, let storiesMaxId, let color, let backgroundEmojiId): + case .channel(let flags, let flags2, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount, let usernames, let storiesMaxId, let color): if boxed { - buffer.appendInt32(427944574) + buffer.appendInt32(-1903702824) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags2, buffer: buffer, boxed: false) @@ -557,8 +557,7 @@ public extension Api { item.serialize(buffer, true) }} if Int(flags2) & Int(1 << 4) != 0 {serializeInt32(storiesMaxId!, buffer: buffer, boxed: false)} - if Int(flags2) & Int(1 << 6) != 0 {serializeInt32(color!, buffer: buffer, boxed: false)} - if Int(flags2) & Int(1 << 5) != 0 {serializeInt64(backgroundEmojiId!, buffer: buffer, boxed: false)} + if Int(flags2) & Int(1 << 7) != 0 {color!.serialize(buffer, true)} break case .channelForbidden(let flags, let id, let accessHash, let title, let untilDate): if boxed { @@ -603,8 +602,8 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .channel(let flags, let flags2, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount, let usernames, let storiesMaxId, let color, let backgroundEmojiId): - return ("channel", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("title", title as Any), ("username", username as Any), ("photo", photo as Any), ("date", date as Any), ("restrictionReason", restrictionReason as Any), ("adminRights", adminRights as Any), ("bannedRights", bannedRights as Any), ("defaultBannedRights", defaultBannedRights as Any), ("participantsCount", participantsCount as Any), ("usernames", usernames as Any), ("storiesMaxId", storiesMaxId as Any), ("color", color as Any), ("backgroundEmojiId", backgroundEmojiId as Any)]) + case .channel(let flags, let flags2, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount, let usernames, let storiesMaxId, let color): + return ("channel", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("title", title as Any), ("username", username as Any), ("photo", photo as Any), ("date", date as Any), ("restrictionReason", restrictionReason as Any), ("adminRights", adminRights as Any), ("bannedRights", bannedRights as Any), ("defaultBannedRights", defaultBannedRights as Any), ("participantsCount", participantsCount as Any), ("usernames", usernames as Any), ("storiesMaxId", storiesMaxId as Any), ("color", color as Any)]) case .channelForbidden(let flags, let id, let accessHash, let title, let untilDate): return ("channelForbidden", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("title", title as Any), ("untilDate", untilDate as Any)]) case .chat(let flags, let id, let title, let photo, let participantsCount, let date, let version, let migratedTo, let adminRights, let defaultBannedRights): @@ -659,10 +658,10 @@ public extension Api { } } var _15: Int32? if Int(_2!) & Int(1 << 4) != 0 {_15 = reader.readInt32() } - var _16: Int32? - if Int(_2!) & Int(1 << 6) != 0 {_16 = reader.readInt32() } - var _17: Int64? - if Int(_2!) & Int(1 << 5) != 0 {_17 = reader.readInt64() } + var _16: Api.PeerColor? + if Int(_2!) & Int(1 << 7) != 0 {if let signature = reader.readInt32() { + _16 = Api.parse(reader, signature: signature) as? Api.PeerColor + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -678,10 +677,9 @@ public extension Api { let _c13 = (Int(_1!) & Int(1 << 17) == 0) || _13 != nil let _c14 = (Int(_2!) & Int(1 << 0) == 0) || _14 != nil let _c15 = (Int(_2!) & Int(1 << 4) == 0) || _15 != nil - let _c16 = (Int(_2!) & Int(1 << 6) == 0) || _16 != nil - let _c17 = (Int(_2!) & Int(1 << 5) == 0) || _17 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 { - return Api.Chat.channel(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, title: _5!, username: _6, photo: _7!, date: _8!, restrictionReason: _9, adminRights: _10, bannedRights: _11, defaultBannedRights: _12, participantsCount: _13, usernames: _14, storiesMaxId: _15, color: _16, backgroundEmojiId: _17) + let _c16 = (Int(_2!) & Int(1 << 7) == 0) || _16 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 { + return Api.Chat.channel(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, title: _5!, username: _6, photo: _7!, date: _8!, restrictionReason: _9, adminRights: _10, bannedRights: _11, defaultBannedRights: _12, participantsCount: _13, usernames: _14, storiesMaxId: _15, color: _16) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api30.swift b/submodules/TelegramApi/Sources/Api30.swift index 10815203ddf..c88196f1820 100644 --- a/submodules/TelegramApi/Sources/Api30.swift +++ b/submodules/TelegramApi/Sources/Api30.swift @@ -1,29 +1,153 @@ -public extension Api.upload { - enum File: TypeConstructorDescription { - case file(type: Api.storage.FileType, mtime: Int32, bytes: Buffer) - case fileCdnRedirect(dcId: Int32, fileToken: Buffer, encryptionKey: Buffer, encryptionIv: Buffer, fileHashes: [Api.FileHash]) +public extension Api.phone { + enum GroupParticipants: TypeConstructorDescription { + case groupParticipants(count: Int32, participants: [Api.GroupCallParticipant], nextOffset: String, chats: [Api.Chat], users: [Api.User], version: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .file(let type, let mtime, let bytes): + case .groupParticipants(let count, let participants, let nextOffset, let chats, let users, let version): if boxed { - buffer.appendInt32(157948117) + buffer.appendInt32(-193506890) + } + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(participants.count)) + for item in participants { + item.serialize(buffer, true) + } + serializeString(nextOffset, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + serializeInt32(version, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .groupParticipants(let count, let participants, let nextOffset, let chats, let users, let version): + return ("groupParticipants", [("count", count as Any), ("participants", participants as Any), ("nextOffset", nextOffset as Any), ("chats", chats as Any), ("users", users as Any), ("version", version as Any)]) + } + } + + public static func parse_groupParticipants(_ reader: BufferReader) -> GroupParticipants? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.GroupCallParticipant]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.GroupCallParticipant.self) + } + var _3: String? + _3 = parseString(reader) + var _4: [Api.Chat]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _5: [Api.User]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + var _6: Int32? + _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 = _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.phone.GroupParticipants.groupParticipants(count: _1!, participants: _2!, nextOffset: _3!, chats: _4!, users: _5!, version: _6!) + } + else { + return nil + } + } + + } +} +public extension Api.phone { + enum JoinAsPeers: TypeConstructorDescription { + case joinAsPeers(peers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .joinAsPeers(let peers, let chats, let users): + if boxed { + buffer.appendInt32(-1343921601) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peers.count)) + for item in peers { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) } - type.serialize(buffer, true) - serializeInt32(mtime, buffer: buffer, boxed: false) - serializeBytes(bytes, buffer: buffer, boxed: false) break - case .fileCdnRedirect(let dcId, let fileToken, let encryptionKey, let encryptionIv, let fileHashes): + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .joinAsPeers(let peers, let chats, let users): + return ("joinAsPeers", [("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_joinAsPeers(_ reader: BufferReader) -> JoinAsPeers? { + var _1: [Api.Peer]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.phone.JoinAsPeers.joinAsPeers(peers: _1!, chats: _2!, users: _3!) + } + else { + return nil + } + } + + } +} +public extension Api.phone { + enum PhoneCall: TypeConstructorDescription { + case phoneCall(phoneCall: Api.PhoneCall, users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .phoneCall(let phoneCall, let users): if boxed { - buffer.appendInt32(-242427324) + buffer.appendInt32(-326966976) } - serializeInt32(dcId, buffer: buffer, boxed: false) - serializeBytes(fileToken, buffer: buffer, boxed: false) - serializeBytes(encryptionKey, buffer: buffer, boxed: false) - serializeBytes(encryptionIv, buffer: buffer, boxed: false) + phoneCall.serialize(buffer, true) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(fileHashes.count)) - for item in fileHashes { + buffer.appendInt32(Int32(users.count)) + for item in users { item.serialize(buffer, true) } break @@ -32,52 +156,228 @@ public extension Api.upload { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .file(let type, let mtime, let bytes): - return ("file", [("type", type as Any), ("mtime", mtime as Any), ("bytes", bytes as Any)]) - case .fileCdnRedirect(let dcId, let fileToken, let encryptionKey, let encryptionIv, let fileHashes): - return ("fileCdnRedirect", [("dcId", dcId as Any), ("fileToken", fileToken as Any), ("encryptionKey", encryptionKey as Any), ("encryptionIv", encryptionIv as Any), ("fileHashes", fileHashes as Any)]) + case .phoneCall(let phoneCall, let users): + return ("phoneCall", [("phoneCall", phoneCall as Any), ("users", users as Any)]) } } - public static func parse_file(_ reader: BufferReader) -> File? { - var _1: Api.storage.FileType? + public static func parse_phoneCall(_ reader: BufferReader) -> PhoneCall? { + var _1: Api.PhoneCall? if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.storage.FileType + _1 = Api.parse(reader, signature: signature) as? Api.PhoneCall + } + var _2: [Api.User]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.phone.PhoneCall.phoneCall(phoneCall: _1!, users: _2!) + } + else { + return nil + } + } + + } +} +public extension Api.photos { + enum Photo: TypeConstructorDescription { + case photo(photo: Api.Photo, users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .photo(let photo, let users): + if boxed { + buffer.appendInt32(539045032) + } + photo.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .photo(let photo, let users): + return ("photo", [("photo", photo as Any), ("users", users as Any)]) + } + } + + public static func parse_photo(_ reader: BufferReader) -> Photo? { + var _1: Api.Photo? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Photo + } + var _2: [Api.User]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.photos.Photo.photo(photo: _1!, users: _2!) + } + else { + return nil + } + } + + } +} +public extension Api.photos { + enum Photos: TypeConstructorDescription { + case photos(photos: [Api.Photo], users: [Api.User]) + case photosSlice(count: Int32, photos: [Api.Photo], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .photos(let photos, let users): + if boxed { + buffer.appendInt32(-1916114267) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(photos.count)) + for item in photos { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + case .photosSlice(let count, let photos, let users): + if boxed { + buffer.appendInt32(352657236) + } + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(photos.count)) + for item in photos { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .photos(let photos, let users): + return ("photos", [("photos", photos as Any), ("users", users as Any)]) + case .photosSlice(let count, let photos, let users): + return ("photosSlice", [("count", count as Any), ("photos", photos as Any), ("users", users as Any)]) + } + } + + public static func parse_photos(_ reader: BufferReader) -> Photos? { + var _1: [Api.Photo]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Photo.self) + } + var _2: [Api.User]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.photos.Photos.photos(photos: _1!, users: _2!) + } + else { + return nil + } + } + public static func parse_photosSlice(_ reader: BufferReader) -> Photos? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.Photo]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Photo.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } - var _2: Int32? - _2 = reader.readInt32() - var _3: Buffer? - _3 = parseBytes(reader) let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil if _c1 && _c2 && _c3 { - return Api.upload.File.file(type: _1!, mtime: _2!, bytes: _3!) + return Api.photos.Photos.photosSlice(count: _1!, photos: _2!, users: _3!) } else { return nil } } - public static func parse_fileCdnRedirect(_ reader: BufferReader) -> File? { + + } +} +public extension Api.premium { + enum BoostsList: TypeConstructorDescription { + case boostsList(flags: Int32, count: Int32, boosts: [Api.Boost], nextOffset: String?, users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .boostsList(let flags, let count, let boosts, let nextOffset, let users): + if boxed { + buffer.appendInt32(-2030542532) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(boosts.count)) + for item in boosts { + item.serialize(buffer, true) + } + if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .boostsList(let flags, let count, let boosts, let nextOffset, let users): + return ("boostsList", [("flags", flags as Any), ("count", count as Any), ("boosts", boosts as Any), ("nextOffset", nextOffset as Any), ("users", users as Any)]) + } + } + + public static func parse_boostsList(_ reader: BufferReader) -> BoostsList? { var _1: Int32? _1 = reader.readInt32() - var _2: Buffer? - _2 = parseBytes(reader) - var _3: Buffer? - _3 = parseBytes(reader) - var _4: Buffer? - _4 = parseBytes(reader) - var _5: [Api.FileHash]? + var _2: Int32? + _2 = reader.readInt32() + var _3: [Api.Boost]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Boost.self) + } + var _4: String? + if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) } + var _5: [Api.User]? if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.FileHash.self) + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - let _c4 = _4 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = _5 != nil if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.upload.File.fileCdnRedirect(dcId: _1!, fileToken: _2!, encryptionKey: _3!, encryptionIv: _4!, fileHashes: _5!) + return Api.premium.BoostsList.boostsList(flags: _1!, count: _2!, boosts: _3!, nextOffset: _4, users: _5!) } else { return nil @@ -86,52 +386,84 @@ public extension Api.upload { } } -public extension Api.upload { - enum WebFile: TypeConstructorDescription { - case webFile(size: Int32, mimeType: String, fileType: Api.storage.FileType, mtime: Int32, bytes: Buffer) +public extension Api.premium { + enum BoostsStatus: TypeConstructorDescription { + case boostsStatus(flags: Int32, level: Int32, currentLevelBoosts: Int32, boosts: Int32, giftBoosts: Int32?, nextLevelBoosts: Int32?, premiumAudience: Api.StatsPercentValue?, boostUrl: String, prepaidGiveaways: [Api.PrepaidGiveaway]?, myBoostSlots: [Int32]?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .webFile(let size, let mimeType, let fileType, let mtime, let bytes): + case .boostsStatus(let flags, let level, let currentLevelBoosts, let boosts, let giftBoosts, let nextLevelBoosts, let premiumAudience, let boostUrl, let prepaidGiveaways, let myBoostSlots): if boxed { - buffer.appendInt32(568808380) + buffer.appendInt32(1230586490) } - serializeInt32(size, buffer: buffer, boxed: false) - serializeString(mimeType, buffer: buffer, boxed: false) - fileType.serialize(buffer, true) - serializeInt32(mtime, buffer: buffer, boxed: false) - serializeBytes(bytes, buffer: buffer, boxed: false) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(level, buffer: buffer, boxed: false) + serializeInt32(currentLevelBoosts, buffer: buffer, boxed: false) + serializeInt32(boosts, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 4) != 0 {serializeInt32(giftBoosts!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(nextLevelBoosts!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {premiumAudience!.serialize(buffer, true)} + serializeString(boostUrl, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(prepaidGiveaways!.count)) + for item in prepaidGiveaways! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 2) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(myBoostSlots!.count)) + for item in myBoostSlots! { + serializeInt32(item, buffer: buffer, boxed: false) + }} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .webFile(let size, let mimeType, let fileType, let mtime, let bytes): - return ("webFile", [("size", size as Any), ("mimeType", mimeType as Any), ("fileType", fileType as Any), ("mtime", mtime as Any), ("bytes", bytes as Any)]) + case .boostsStatus(let flags, let level, let currentLevelBoosts, let boosts, let giftBoosts, let nextLevelBoosts, let premiumAudience, let boostUrl, let prepaidGiveaways, let myBoostSlots): + return ("boostsStatus", [("flags", flags as Any), ("level", level as Any), ("currentLevelBoosts", currentLevelBoosts as Any), ("boosts", boosts as Any), ("giftBoosts", giftBoosts as Any), ("nextLevelBoosts", nextLevelBoosts as Any), ("premiumAudience", premiumAudience as Any), ("boostUrl", boostUrl as Any), ("prepaidGiveaways", prepaidGiveaways as Any), ("myBoostSlots", myBoostSlots as Any)]) } } - public static func parse_webFile(_ reader: BufferReader) -> WebFile? { + public static func parse_boostsStatus(_ reader: BufferReader) -> BoostsStatus? { var _1: Int32? _1 = reader.readInt32() - var _2: String? - _2 = parseString(reader) - var _3: Api.storage.FileType? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.storage.FileType - } + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() var _4: Int32? _4 = reader.readInt32() - var _5: Buffer? - _5 = parseBytes(reader) + var _5: Int32? + if Int(_1!) & Int(1 << 4) != 0 {_5 = reader.readInt32() } + var _6: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_6 = reader.readInt32() } + var _7: Api.StatsPercentValue? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.StatsPercentValue + } } + var _8: String? + _8 = parseString(reader) + var _9: [Api.PrepaidGiveaway]? + if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() { + _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PrepaidGiveaway.self) + } } + var _10: [Int32]? + if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() { + _10 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.upload.WebFile.webFile(size: _1!, mimeType: _2!, fileType: _3!, mtime: _4!, bytes: _5!) + let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil + let _c8 = _8 != nil + let _c9 = (Int(_1!) & Int(1 << 3) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 2) == 0) || _10 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { + return Api.premium.BoostsStatus.boostsStatus(flags: _1!, level: _2!, currentLevelBoosts: _3!, boosts: _4!, giftBoosts: _5, nextLevelBoosts: _6, premiumAudience: _7, boostUrl: _8!, prepaidGiveaways: _9, myBoostSlots: _10) } else { return nil @@ -140,17 +472,21 @@ public extension Api.upload { } } -public extension Api.users { - enum UserFull: TypeConstructorDescription { - case userFull(fullUser: Api.UserFull, chats: [Api.Chat], users: [Api.User]) +public extension Api.premium { + enum MyBoosts: TypeConstructorDescription { + case myBoosts(myBoosts: [Api.MyBoost], chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .userFull(let fullUser, let chats, let users): + case .myBoosts(let myBoosts, let chats, let users): if boxed { - buffer.appendInt32(997004590) + buffer.appendInt32(-1696454430) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(myBoosts.count)) + for item in myBoosts { + item.serialize(buffer, true) } - fullUser.serialize(buffer, true) buffer.appendInt32(481674261) buffer.appendInt32(Int32(chats.count)) for item in chats { @@ -167,15 +503,15 @@ public extension Api.users { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .userFull(let fullUser, let chats, let users): - return ("userFull", [("fullUser", fullUser as Any), ("chats", chats as Any), ("users", users as Any)]) + case .myBoosts(let myBoosts, let chats, let users): + return ("myBoosts", [("myBoosts", myBoosts as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_userFull(_ reader: BufferReader) -> UserFull? { - var _1: Api.UserFull? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.UserFull + public static func parse_myBoosts(_ reader: BufferReader) -> MyBoosts? { + var _1: [Api.MyBoost]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MyBoost.self) } var _2: [Api.Chat]? if let _ = reader.readInt32() { @@ -189,7 +525,1185 @@ public extension Api.users { let _c2 = _2 != nil let _c3 = _3 != nil if _c1 && _c2 && _c3 { - return Api.users.UserFull.userFull(fullUser: _1!, chats: _2!, users: _3!) + return Api.premium.MyBoosts.myBoosts(myBoosts: _1!, chats: _2!, users: _3!) + } + 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]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .megagroupStats(let period, let members, let messages, let viewers, let posters, let growthGraph, let membersGraph, let newMembersBySourceGraph, let languagesGraph, let messagesGraph, let actionsGraph, let topHoursGraph, let weekdaysGraph, let topPosters, let topAdmins, let topInviters, let users): + if boxed { + buffer.appendInt32(-276825834) + } + period.serialize(buffer, true) + members.serialize(buffer, true) + messages.serialize(buffer, true) + viewers.serialize(buffer, true) + posters.serialize(buffer, true) + growthGraph.serialize(buffer, true) + membersGraph.serialize(buffer, true) + newMembersBySourceGraph.serialize(buffer, true) + languagesGraph.serialize(buffer, true) + messagesGraph.serialize(buffer, true) + actionsGraph.serialize(buffer, true) + topHoursGraph.serialize(buffer, true) + weekdaysGraph.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(topPosters.count)) + for item in topPosters { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(topAdmins.count)) + for item in topAdmins { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(topInviters.count)) + for item in topInviters { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .megagroupStats(let period, let members, let messages, let viewers, let posters, let growthGraph, let membersGraph, let newMembersBySourceGraph, let languagesGraph, let messagesGraph, let actionsGraph, let topHoursGraph, let weekdaysGraph, let topPosters, let topAdmins, let topInviters, let users): + return ("megagroupStats", [("period", period as Any), ("members", members as Any), ("messages", messages as Any), ("viewers", viewers as Any), ("posters", posters as Any), ("growthGraph", growthGraph as Any), ("membersGraph", membersGraph as Any), ("newMembersBySourceGraph", newMembersBySourceGraph as Any), ("languagesGraph", languagesGraph as Any), ("messagesGraph", messagesGraph as Any), ("actionsGraph", actionsGraph as Any), ("topHoursGraph", topHoursGraph as Any), ("weekdaysGraph", weekdaysGraph as Any), ("topPosters", topPosters as Any), ("topAdmins", topAdmins as Any), ("topInviters", topInviters as Any), ("users", users as Any)]) + } + } + + public static func parse_megagroupStats(_ reader: BufferReader) -> MegagroupStats? { + 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.StatsGraph? + if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _7: Api.StatsGraph? + if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _8: Api.StatsGraph? + if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _9: Api.StatsGraph? + if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + 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.StatsGroupTopPoster]? + if let _ = reader.readInt32() { + _14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopPoster.self) + } + var _15: [Api.StatsGroupTopAdmin]? + if let _ = reader.readInt32() { + _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopAdmin.self) + } + var _16: [Api.StatsGroupTopInviter]? + if let _ = reader.readInt32() { + _16 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopInviter.self) + } + var _17: [Api.User]? + if let _ = reader.readInt32() { + _17 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _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 + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 { + return Api.stats.MegagroupStats.megagroupStats(period: _1!, members: _2!, messages: _3!, viewers: _4!, posters: _5!, growthGraph: _6!, membersGraph: _7!, newMembersBySourceGraph: _8!, languagesGraph: _9!, messagesGraph: _10!, actionsGraph: _11!, topHoursGraph: _12!, weekdaysGraph: _13!, topPosters: _14!, topAdmins: _15!, topInviters: _16!, users: _17!) + } + else { + return nil + } + } + + } +} +public extension Api.stats { + enum MessageStats: TypeConstructorDescription { + case messageStats(viewsGraph: Api.StatsGraph, reactionsByEmotionGraph: Api.StatsGraph) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .messageStats(let viewsGraph, let reactionsByEmotionGraph): + if boxed { + buffer.appendInt32(2145983508) + } + viewsGraph.serialize(buffer, true) + reactionsByEmotionGraph.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .messageStats(let viewsGraph, let reactionsByEmotionGraph): + return ("messageStats", [("viewsGraph", viewsGraph as Any), ("reactionsByEmotionGraph", reactionsByEmotionGraph as Any)]) + } + } + + public static func parse_messageStats(_ reader: BufferReader) -> MessageStats? { + var _1: Api.StatsGraph? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _2: Api.StatsGraph? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.stats.MessageStats.messageStats(viewsGraph: _1!, reactionsByEmotionGraph: _2!) + } + else { + return nil + } + } + + } +} +public extension Api.stats { + enum PublicForwards: TypeConstructorDescription { + case publicForwards(flags: Int32, count: Int32, forwards: [Api.PublicForward], nextOffset: String?, chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .publicForwards(let flags, let count, let forwards, let nextOffset, let chats, let users): + if boxed { + buffer.appendInt32(-1828487648) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(forwards.count)) + for item in forwards { + item.serialize(buffer, true) + } + if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .publicForwards(let flags, let count, let forwards, let nextOffset, let chats, let users): + return ("publicForwards", [("flags", flags as Any), ("count", count as Any), ("forwards", forwards as Any), ("nextOffset", nextOffset as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_publicForwards(_ reader: BufferReader) -> PublicForwards? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: [Api.PublicForward]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PublicForward.self) + } + var _4: String? + if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) } + var _5: [Api.Chat]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _6: [Api.User]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.stats.PublicForwards.publicForwards(flags: _1!, count: _2!, forwards: _3!, nextOffset: _4, chats: _5!, users: _6!) + } + else { + return nil + } + } + + } +} +public extension Api.stats { + enum StoryStats: TypeConstructorDescription { + case storyStats(viewsGraph: Api.StatsGraph, reactionsByEmotionGraph: Api.StatsGraph) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .storyStats(let viewsGraph, let reactionsByEmotionGraph): + if boxed { + buffer.appendInt32(1355613820) + } + viewsGraph.serialize(buffer, true) + reactionsByEmotionGraph.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .storyStats(let viewsGraph, let reactionsByEmotionGraph): + return ("storyStats", [("viewsGraph", viewsGraph as Any), ("reactionsByEmotionGraph", reactionsByEmotionGraph as Any)]) + } + } + + public static func parse_storyStats(_ reader: BufferReader) -> StoryStats? { + var _1: Api.StatsGraph? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _2: Api.StatsGraph? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.stats.StoryStats.storyStats(viewsGraph: _1!, reactionsByEmotionGraph: _2!) + } + else { + return nil + } + } + + } +} +public extension Api.stickers { + enum SuggestedShortName: TypeConstructorDescription { + case suggestedShortName(shortName: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .suggestedShortName(let shortName): + if boxed { + buffer.appendInt32(-2046910401) + } + serializeString(shortName, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .suggestedShortName(let shortName): + return ("suggestedShortName", [("shortName", shortName as Any)]) + } + } + + public static func parse_suggestedShortName(_ reader: BufferReader) -> SuggestedShortName? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.stickers.SuggestedShortName.suggestedShortName(shortName: _1!) + } + else { + return nil + } + } + + } +} +public extension Api.storage { + enum FileType: TypeConstructorDescription { + case fileGif + case fileJpeg + case fileMov + case fileMp3 + case fileMp4 + case filePartial + case filePdf + case filePng + case fileUnknown + case fileWebp + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .fileGif: + if boxed { + buffer.appendInt32(-891180321) + } + + break + case .fileJpeg: + if boxed { + buffer.appendInt32(8322574) + } + + break + case .fileMov: + if boxed { + buffer.appendInt32(1258941372) + } + + break + case .fileMp3: + if boxed { + buffer.appendInt32(1384777335) + } + + break + case .fileMp4: + if boxed { + buffer.appendInt32(-1278304028) + } + + break + case .filePartial: + if boxed { + buffer.appendInt32(1086091090) + } + + break + case .filePdf: + if boxed { + buffer.appendInt32(-1373745011) + } + + break + case .filePng: + if boxed { + buffer.appendInt32(172975040) + } + + break + case .fileUnknown: + if boxed { + buffer.appendInt32(-1432995067) + } + + break + case .fileWebp: + if boxed { + buffer.appendInt32(276907596) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .fileGif: + return ("fileGif", []) + case .fileJpeg: + return ("fileJpeg", []) + case .fileMov: + return ("fileMov", []) + case .fileMp3: + return ("fileMp3", []) + case .fileMp4: + return ("fileMp4", []) + case .filePartial: + return ("filePartial", []) + case .filePdf: + return ("filePdf", []) + case .filePng: + return ("filePng", []) + case .fileUnknown: + return ("fileUnknown", []) + case .fileWebp: + return ("fileWebp", []) + } + } + + public static func parse_fileGif(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.fileGif + } + public static func parse_fileJpeg(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.fileJpeg + } + public static func parse_fileMov(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.fileMov + } + public static func parse_fileMp3(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.fileMp3 + } + public static func parse_fileMp4(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.fileMp4 + } + public static func parse_filePartial(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.filePartial + } + public static func parse_filePdf(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.filePdf + } + public static func parse_filePng(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.filePng + } + public static func parse_fileUnknown(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.fileUnknown + } + public static func parse_fileWebp(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.fileWebp + } + + } +} +public extension Api.stories { + enum AllStories: TypeConstructorDescription { + case allStories(flags: Int32, count: Int32, state: String, peerStories: [Api.PeerStories], chats: [Api.Chat], users: [Api.User], stealthMode: Api.StoriesStealthMode) + case allStoriesNotModified(flags: Int32, state: String, stealthMode: Api.StoriesStealthMode) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .allStories(let flags, let count, let state, let peerStories, let chats, let users, let stealthMode): + if boxed { + buffer.appendInt32(1862033025) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + serializeString(state, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peerStories.count)) + for item in peerStories { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + stealthMode.serialize(buffer, true) + break + case .allStoriesNotModified(let flags, let state, let stealthMode): + if boxed { + buffer.appendInt32(291044926) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(state, buffer: buffer, boxed: false) + stealthMode.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .allStories(let flags, let count, let state, let peerStories, let chats, let users, let stealthMode): + return ("allStories", [("flags", flags as Any), ("count", count as Any), ("state", state as Any), ("peerStories", peerStories as Any), ("chats", chats as Any), ("users", users as Any), ("stealthMode", stealthMode as Any)]) + case .allStoriesNotModified(let flags, let state, let stealthMode): + return ("allStoriesNotModified", [("flags", flags as Any), ("state", state as Any), ("stealthMode", stealthMode as Any)]) + } + } + + public static func parse_allStories(_ reader: BufferReader) -> AllStories? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: String? + _3 = parseString(reader) + var _4: [Api.PeerStories]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PeerStories.self) + } + var _5: [Api.Chat]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _6: [Api.User]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + var _7: Api.StoriesStealthMode? + if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.StoriesStealthMode + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.stories.AllStories.allStories(flags: _1!, count: _2!, state: _3!, peerStories: _4!, chats: _5!, users: _6!, stealthMode: _7!) + } + else { + return nil + } + } + public static func parse_allStoriesNotModified(_ reader: BufferReader) -> AllStories? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + var _3: Api.StoriesStealthMode? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.StoriesStealthMode + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.stories.AllStories.allStoriesNotModified(flags: _1!, state: _2!, stealthMode: _3!) + } + else { + return nil + } + } + + } +} +public extension Api.stories { + enum PeerStories: TypeConstructorDescription { + case peerStories(stories: Api.PeerStories, chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .peerStories(let stories, let chats, let users): + if boxed { + buffer.appendInt32(-890861720) + } + stories.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .peerStories(let stories, let chats, let users): + return ("peerStories", [("stories", stories as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_peerStories(_ reader: BufferReader) -> PeerStories? { + var _1: Api.PeerStories? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.PeerStories + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.stories.PeerStories.peerStories(stories: _1!, chats: _2!, users: _3!) + } + else { + return nil + } + } + + } +} +public extension Api.stories { + enum Stories: TypeConstructorDescription { + case stories(count: Int32, stories: [Api.StoryItem], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .stories(let count, let stories, let chats, let users): + if boxed { + buffer.appendInt32(1574486984) + } + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(stories.count)) + for item in stories { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .stories(let count, let stories, let chats, let users): + return ("stories", [("count", count as Any), ("stories", stories as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_stories(_ reader: BufferReader) -> Stories? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.StoryItem]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryItem.self) + } + var _3: [Api.Chat]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _4: [Api.User]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.stories.Stories.stories(count: _1!, stories: _2!, chats: _3!, users: _4!) + } + else { + return nil + } + } + + } +} +public extension Api.stories { + enum StoryViews: TypeConstructorDescription { + case storyViews(views: [Api.StoryViews], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .storyViews(let views, let users): + if boxed { + buffer.appendInt32(-560009955) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(views.count)) + for item in views { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .storyViews(let views, let users): + return ("storyViews", [("views", views as Any), ("users", users as Any)]) + } + } + + public static func parse_storyViews(_ reader: BufferReader) -> StoryViews? { + var _1: [Api.StoryViews]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryViews.self) + } + var _2: [Api.User]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.stories.StoryViews.storyViews(views: _1!, users: _2!) + } + else { + return nil + } + } + + } +} +public extension Api.stories { + enum StoryViewsList: TypeConstructorDescription { + case storyViewsList(flags: Int32, count: Int32, reactionsCount: Int32, views: [Api.StoryView], users: [Api.User], nextOffset: String?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .storyViewsList(let flags, let count, let reactionsCount, let views, let users, let nextOffset): + if boxed { + buffer.appendInt32(1189722604) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + serializeInt32(reactionsCount, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(views.count)) + for item in views { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .storyViewsList(let flags, let count, let reactionsCount, let views, let users, let nextOffset): + return ("storyViewsList", [("flags", flags as Any), ("count", count as Any), ("reactionsCount", reactionsCount as Any), ("views", views as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)]) + } + } + + public static func parse_storyViewsList(_ reader: BufferReader) -> StoryViewsList? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: [Api.StoryView]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryView.self) + } + var _5: [Api.User]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + var _6: String? + if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.stories.StoryViewsList.storyViewsList(flags: _1!, count: _2!, reactionsCount: _3!, views: _4!, users: _5!, nextOffset: _6) + } + else { + return nil + } + } + + } +} +public extension Api.updates { + indirect enum ChannelDifference: TypeConstructorDescription { + case channelDifference(flags: Int32, pts: Int32, timeout: Int32?, newMessages: [Api.Message], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User]) + case channelDifferenceEmpty(flags: Int32, pts: Int32, timeout: Int32?) + case channelDifferenceTooLong(flags: Int32, timeout: Int32?, dialog: Api.Dialog, messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .channelDifference(let flags, let pts, let timeout, let newMessages, let otherUpdates, let chats, let users): + if boxed { + buffer.appendInt32(543450958) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(pts, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(newMessages.count)) + for item in newMessages { + 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) + } + break + case .channelDifferenceEmpty(let flags, let pts, let timeout): + if boxed { + buffer.appendInt32(1041346555) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(pts, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)} + break + case .channelDifferenceTooLong(let flags, let timeout, let dialog, let messages, let chats, let users): + if boxed { + buffer.appendInt32(-1531132162) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)} + dialog.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .channelDifference(let flags, let pts, let timeout, let newMessages, let otherUpdates, let chats, let users): + return ("channelDifference", [("flags", flags as Any), ("pts", pts as Any), ("timeout", timeout as Any), ("newMessages", newMessages as Any), ("otherUpdates", otherUpdates as Any), ("chats", chats as Any), ("users", users as Any)]) + case .channelDifferenceEmpty(let flags, let pts, let timeout): + return ("channelDifferenceEmpty", [("flags", flags as Any), ("pts", pts as Any), ("timeout", timeout as Any)]) + case .channelDifferenceTooLong(let flags, let timeout, let dialog, let messages, let chats, let users): + return ("channelDifferenceTooLong", [("flags", flags as Any), ("timeout", timeout as Any), ("dialog", dialog as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_channelDifference(_ reader: BufferReader) -> ChannelDifference? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() } + var _4: [Api.Message]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _5: [Api.Update]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Update.self) + } + var _6: [Api.Chat]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _7: [Api.User]? + if let _ = reader.readInt32() { + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _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.updates.ChannelDifference.channelDifference(flags: _1!, pts: _2!, timeout: _3, newMessages: _4!, otherUpdates: _5!, chats: _6!, users: _7!) + } + else { + return nil + } + } + public static func parse_channelDifferenceEmpty(_ reader: BufferReader) -> ChannelDifference? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.updates.ChannelDifference.channelDifferenceEmpty(flags: _1!, pts: _2!, timeout: _3) + } + else { + return nil + } + } + public static func parse_channelDifferenceTooLong(_ reader: BufferReader) -> ChannelDifference? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() } + var _3: Api.Dialog? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.Dialog + } + var _4: [Api.Message]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _5: [Api.Chat]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _6: [Api.User]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _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.ChannelDifference.channelDifferenceTooLong(flags: _1!, timeout: _2, dialog: _3!, messages: _4!, chats: _5!, users: _6!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api31.swift b/submodules/TelegramApi/Sources/Api31.swift index 881d3faef4d..f6d0dc14fb8 100644 --- a/submodules/TelegramApi/Sources/Api31.swift +++ b/submodules/TelegramApi/Sources/Api31.swift @@ -1,9384 +1,512 @@ -public extension Api.functions.account { - static func acceptAuthorization(botId: Int64, scope: String, publicKey: String, valueHashes: [Api.SecureValueHash], credentials: Api.SecureCredentialsEncrypted) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-202552205) - serializeInt64(botId, buffer: buffer, boxed: false) - serializeString(scope, buffer: buffer, boxed: false) - serializeString(publicKey, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(valueHashes.count)) - for item in valueHashes { - item.serialize(buffer, true) - } - credentials.serialize(buffer, true) - return (FunctionDescription(name: "account.acceptAuthorization", parameters: [("botId", String(describing: botId)), ("scope", String(describing: scope)), ("publicKey", String(describing: publicKey)), ("valueHashes", String(describing: valueHashes)), ("credentials", String(describing: credentials))]), 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 cancelPasswordEmail() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1043606090) - - return (FunctionDescription(name: "account.cancelPasswordEmail", parameters: []), 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 changeAuthorizationSettings(flags: Int32, hash: Int64, encryptedRequestsDisabled: Api.Bool?, callRequestsDisabled: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1089766498) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {encryptedRequestsDisabled!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {callRequestsDisabled!.serialize(buffer, true)} - return (FunctionDescription(name: "account.changeAuthorizationSettings", parameters: [("flags", String(describing: flags)), ("hash", String(describing: hash)), ("encryptedRequestsDisabled", String(describing: encryptedRequestsDisabled)), ("callRequestsDisabled", String(describing: callRequestsDisabled))]), 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 changePhone(phoneNumber: String, phoneCodeHash: String, phoneCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1891839707) - serializeString(phoneNumber, buffer: buffer, boxed: false) - serializeString(phoneCodeHash, buffer: buffer, boxed: false) - serializeString(phoneCode, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.changePhone", parameters: [("phoneNumber", String(describing: phoneNumber)), ("phoneCodeHash", String(describing: phoneCodeHash)), ("phoneCode", String(describing: phoneCode))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.User? in - let reader = BufferReader(buffer) - var result: Api.User? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.User - } - return result - }) - } -} -public extension Api.functions.account { - static func checkUsername(username: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(655677548) - serializeString(username, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.checkUsername", parameters: [("username", String(describing: username))]), 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 clearRecentEmojiStatuses() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(404757166) - - return (FunctionDescription(name: "account.clearRecentEmojiStatuses", parameters: []), 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 confirmPasswordEmail(code: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1881204448) - serializeString(code, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.confirmPasswordEmail", parameters: [("code", String(describing: code))]), 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 confirmPhone(phoneCodeHash: String, phoneCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1596029123) - serializeString(phoneCodeHash, buffer: buffer, boxed: false) - serializeString(phoneCode, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.confirmPhone", parameters: [("phoneCodeHash", String(describing: phoneCodeHash)), ("phoneCode", String(describing: phoneCode))]), 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 createTheme(flags: Int32, slug: String, title: String, document: Api.InputDocument?, settings: [Api.InputThemeSettings]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1697530880) - serializeInt32(flags, 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) - }} - return (FunctionDescription(name: "account.createTheme", parameters: [("flags", String(describing: flags)), ("slug", String(describing: slug)), ("title", String(describing: title)), ("document", String(describing: document)), ("settings", String(describing: settings))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Theme? in - let reader = BufferReader(buffer) - var result: Api.Theme? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Theme - } - return result - }) - } -} -public extension Api.functions.account { - static func declinePasswordReset() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1284770294) - - return (FunctionDescription(name: "account.declinePasswordReset", parameters: []), 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 deleteAccount(flags: Int32, reason: String, password: Api.InputCheckPasswordSRP?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1564422284) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(reason, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {password!.serialize(buffer, true)} - return (FunctionDescription(name: "account.deleteAccount", parameters: [("flags", String(describing: flags)), ("reason", String(describing: reason)), ("password", String(describing: password))]), 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 deleteAutoSaveExceptions() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1404829728) - - return (FunctionDescription(name: "account.deleteAutoSaveExceptions", parameters: []), 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 deleteSecureValue(types: [Api.SecureValueType]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1199522741) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(types.count)) - for item in types { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "account.deleteSecureValue", parameters: [("types", String(describing: types))]), 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 finishTakeoutSession(flags: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(489050862) - serializeInt32(flags, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.finishTakeoutSession", parameters: [("flags", String(describing: flags))]), 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 getAccountTTL() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(150761757) - - return (FunctionDescription(name: "account.getAccountTTL", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.AccountDaysTTL? in - let reader = BufferReader(buffer) - var result: Api.AccountDaysTTL? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.AccountDaysTTL - } - return result - }) - } -} -public extension Api.functions.account { - static func getAllSecureValues() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.SecureValue]>) { - let buffer = Buffer() - buffer.appendInt32(-1299661699) - - return (FunctionDescription(name: "account.getAllSecureValues", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.SecureValue]? in - let reader = BufferReader(buffer) - var result: [Api.SecureValue]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureValue.self) - } - return result - }) - } -} -public extension Api.functions.account { - static func getAuthorizationForm(botId: Int64, scope: String, publicKey: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1456907910) - serializeInt64(botId, buffer: buffer, boxed: false) - serializeString(scope, buffer: buffer, boxed: false) - serializeString(publicKey, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.getAuthorizationForm", parameters: [("botId", String(describing: botId)), ("scope", String(describing: scope)), ("publicKey", String(describing: publicKey))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.AuthorizationForm? in - let reader = BufferReader(buffer) - var result: Api.account.AuthorizationForm? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.AuthorizationForm - } - return result - }) - } -} -public extension Api.functions.account { - static func getAuthorizations() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-484392616) - - return (FunctionDescription(name: "account.getAuthorizations", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.Authorizations? in - let reader = BufferReader(buffer) - var result: Api.account.Authorizations? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.Authorizations - } - return result - }) - } -} -public extension Api.functions.account { - static func getAutoDownloadSettings() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1457130303) - - return (FunctionDescription(name: "account.getAutoDownloadSettings", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.AutoDownloadSettings? in - let reader = BufferReader(buffer) - var result: Api.account.AutoDownloadSettings? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.AutoDownloadSettings - } - return result - }) - } -} -public extension Api.functions.account { - static func getAutoSaveSettings() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1379156774) - - return (FunctionDescription(name: "account.getAutoSaveSettings", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.AutoSaveSettings? in - let reader = BufferReader(buffer) - var result: Api.account.AutoSaveSettings? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.AutoSaveSettings - } - return result - }) - } -} -public extension Api.functions.account { - static func getChatThemes(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-700916087) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.getChatThemes", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.Themes? in - let reader = BufferReader(buffer) - var result: Api.account.Themes? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.Themes - } - return result - }) - } -} -public extension Api.functions.account { - static func getContactSignUpNotification() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1626880216) - - return (FunctionDescription(name: "account.getContactSignUpNotification", parameters: []), 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 getContentSettings() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1952756306) - - return (FunctionDescription(name: "account.getContentSettings", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.ContentSettings? in - let reader = BufferReader(buffer) - var result: Api.account.ContentSettings? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.ContentSettings - } - return result - }) - } -} -public extension Api.functions.account { - static func getDefaultBackgroundEmojis(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1509246514) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.getDefaultBackgroundEmojis", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EmojiList? in - let reader = BufferReader(buffer) - var result: Api.EmojiList? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.EmojiList - } - return result - }) - } -} -public extension Api.functions.account { - static func getDefaultEmojiStatuses(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-696962170) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.getDefaultEmojiStatuses", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.EmojiStatuses? in - let reader = BufferReader(buffer) - var result: Api.account.EmojiStatuses? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.EmojiStatuses - } - return result - }) - } -} -public extension Api.functions.account { - static func getDefaultGroupPhotoEmojis(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1856479058) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.getDefaultGroupPhotoEmojis", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EmojiList? in - let reader = BufferReader(buffer) - var result: Api.EmojiList? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.EmojiList - } - return result - }) - } -} -public extension Api.functions.account { - static func getDefaultProfilePhotoEmojis(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-495647960) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.getDefaultProfilePhotoEmojis", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EmojiList? in - let reader = BufferReader(buffer) - var result: Api.EmojiList? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.EmojiList - } - return result - }) - } -} -public extension Api.functions.account { - static func getGlobalPrivacySettings() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-349483786) - - return (FunctionDescription(name: "account.getGlobalPrivacySettings", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.GlobalPrivacySettings? in - let reader = BufferReader(buffer) - var result: Api.GlobalPrivacySettings? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.GlobalPrivacySettings - } - return result - }) - } -} -public extension Api.functions.account { - static func getMultiWallPapers(wallpapers: [Api.InputWallPaper]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.WallPaper]>) { - let buffer = Buffer() - buffer.appendInt32(1705865692) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(wallpapers.count)) - for item in wallpapers { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "account.getMultiWallPapers", parameters: [("wallpapers", String(describing: wallpapers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.WallPaper]? in - let reader = BufferReader(buffer) - var result: [Api.WallPaper]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.WallPaper.self) - } - return result - }) - } -} -public extension Api.functions.account { - static func getNotifyExceptions(flags: Int32, peer: Api.InputNotifyPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1398240377) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {peer!.serialize(buffer, true)} - return (FunctionDescription(name: "account.getNotifyExceptions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.account { - static func getNotifySettings(peer: Api.InputNotifyPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(313765169) - peer.serialize(buffer, true) - return (FunctionDescription(name: "account.getNotifySettings", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.PeerNotifySettings? in - let reader = BufferReader(buffer) - var result: Api.PeerNotifySettings? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings - } - return result - }) - } -} -public extension Api.functions.account { - static func getPassword() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1418342645) - - return (FunctionDescription(name: "account.getPassword", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.Password? in - let reader = BufferReader(buffer) - var result: Api.account.Password? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.Password - } - return result - }) - } -} -public extension Api.functions.account { - static func getPasswordSettings(password: Api.InputCheckPasswordSRP) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1663767815) - password.serialize(buffer, true) - return (FunctionDescription(name: "account.getPasswordSettings", parameters: [("password", String(describing: password))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.PasswordSettings? in - let reader = BufferReader(buffer) - var result: Api.account.PasswordSettings? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.PasswordSettings - } - return result - }) - } -} -public extension Api.functions.account { - static func getPrivacy(key: Api.InputPrivacyKey) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-623130288) - key.serialize(buffer, true) - return (FunctionDescription(name: "account.getPrivacy", parameters: [("key", String(describing: key))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.PrivacyRules? in - let reader = BufferReader(buffer) - var result: Api.account.PrivacyRules? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.PrivacyRules - } - return result - }) - } -} -public extension Api.functions.account { - static func getRecentEmojiStatuses(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(257392901) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.getRecentEmojiStatuses", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.EmojiStatuses? in - let reader = BufferReader(buffer) - var result: Api.account.EmojiStatuses? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.EmojiStatuses - } - return result - }) - } -} -public extension Api.functions.account { - static func getSavedRingtones(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-510647672) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.getSavedRingtones", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.SavedRingtones? in - let reader = BufferReader(buffer) - var result: Api.account.SavedRingtones? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.SavedRingtones - } - return result - }) - } -} -public extension Api.functions.account { - static func getSecureValue(types: [Api.SecureValueType]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.SecureValue]>) { - let buffer = Buffer() - buffer.appendInt32(1936088002) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(types.count)) - for item in types { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "account.getSecureValue", parameters: [("types", String(describing: types))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.SecureValue]? in - let reader = BufferReader(buffer) - var result: [Api.SecureValue]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureValue.self) - } - return result - }) - } -} -public extension Api.functions.account { - static func getTheme(format: String, theme: Api.InputTheme) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(978872812) - serializeString(format, buffer: buffer, boxed: false) - theme.serialize(buffer, true) - return (FunctionDescription(name: "account.getTheme", parameters: [("format", String(describing: format)), ("theme", String(describing: theme))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Theme? in - let reader = BufferReader(buffer) - var result: Api.Theme? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Theme - } - return result - }) - } -} -public extension Api.functions.account { - static func getThemes(format: String, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1913054296) - serializeString(format, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.getThemes", parameters: [("format", String(describing: format)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.Themes? in - let reader = BufferReader(buffer) - var result: Api.account.Themes? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.Themes - } - return result - }) - } -} -public extension Api.functions.account { - static func getTmpPassword(password: Api.InputCheckPasswordSRP, period: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1151208273) - password.serialize(buffer, true) - serializeInt32(period, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.getTmpPassword", parameters: [("password", String(describing: password)), ("period", String(describing: period))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.TmpPassword? in - let reader = BufferReader(buffer) - var result: Api.account.TmpPassword? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.TmpPassword - } - return result - }) - } -} -public extension Api.functions.account { - static func getWallPaper(wallpaper: Api.InputWallPaper) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-57811990) - wallpaper.serialize(buffer, true) - return (FunctionDescription(name: "account.getWallPaper", parameters: [("wallpaper", String(describing: wallpaper))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.WallPaper? in - let reader = BufferReader(buffer) - var result: Api.WallPaper? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.WallPaper - } - return result - }) - } -} -public extension Api.functions.account { - static func getWallPapers(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(127302966) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.getWallPapers", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.WallPapers? in - let reader = BufferReader(buffer) - var result: Api.account.WallPapers? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.WallPapers - } - return result - }) - } -} -public extension Api.functions.account { - static func getWebAuthorizations() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(405695855) - - return (FunctionDescription(name: "account.getWebAuthorizations", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.WebAuthorizations? in - let reader = BufferReader(buffer) - var result: Api.account.WebAuthorizations? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.WebAuthorizations - } - return result - }) - } -} -public extension Api.functions.account { - static func initTakeoutSession(flags: Int32, fileMaxSize: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1896617296) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 5) != 0 {serializeInt64(fileMaxSize!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "account.initTakeoutSession", parameters: [("flags", String(describing: flags)), ("fileMaxSize", String(describing: fileMaxSize))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.Takeout? in - let reader = BufferReader(buffer) - var result: Api.account.Takeout? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.Takeout - } - return result - }) - } -} -public extension Api.functions.account { - static func installTheme(flags: Int32, theme: Api.InputTheme?, format: String?, baseTheme: Api.BaseTheme?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-953697477) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {theme!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(format!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {baseTheme!.serialize(buffer, true)} - return (FunctionDescription(name: "account.installTheme", parameters: [("flags", String(describing: flags)), ("theme", String(describing: theme)), ("format", String(describing: format)), ("baseTheme", String(describing: baseTheme))]), 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 installWallPaper(wallpaper: Api.InputWallPaper, settings: Api.WallPaperSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-18000023) - wallpaper.serialize(buffer, true) - settings.serialize(buffer, true) - return (FunctionDescription(name: "account.installWallPaper", parameters: [("wallpaper", String(describing: wallpaper)), ("settings", String(describing: settings))]), 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 invalidateSignInCodes(codes: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-896866118) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(codes.count)) - for item in codes { - serializeString(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "account.invalidateSignInCodes", parameters: [("codes", String(describing: codes))]), 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 registerDevice(flags: Int32, tokenType: Int32, token: String, appSandbox: Api.Bool, secret: Buffer, otherUids: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-326762118) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(tokenType, buffer: buffer, boxed: false) - serializeString(token, buffer: buffer, boxed: false) - appSandbox.serialize(buffer, true) - serializeBytes(secret, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(otherUids.count)) - for item in otherUids { - serializeInt64(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "account.registerDevice", parameters: [("flags", String(describing: flags)), ("tokenType", String(describing: tokenType)), ("token", String(describing: token)), ("appSandbox", String(describing: appSandbox)), ("secret", String(describing: secret)), ("otherUids", String(describing: otherUids))]), 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 reorderUsernames(order: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-279966037) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(order.count)) - for item in order { - serializeString(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "account.reorderUsernames", parameters: [("order", String(describing: order))]), 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 reportPeer(peer: Api.InputPeer, reason: Api.ReportReason, message: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-977650298) - peer.serialize(buffer, true) - reason.serialize(buffer, true) - serializeString(message, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.reportPeer", parameters: [("peer", String(describing: peer)), ("reason", String(describing: reason)), ("message", String(describing: message))]), 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 reportProfilePhoto(peer: Api.InputPeer, photoId: Api.InputPhoto, reason: Api.ReportReason, message: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-91437323) - peer.serialize(buffer, true) - photoId.serialize(buffer, true) - reason.serialize(buffer, true) - serializeString(message, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.reportProfilePhoto", parameters: [("peer", String(describing: peer)), ("photoId", String(describing: photoId)), ("reason", String(describing: reason)), ("message", String(describing: message))]), 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 resendPasswordEmail() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2055154197) - - return (FunctionDescription(name: "account.resendPasswordEmail", parameters: []), 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 resetAuthorization(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-545786948) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.resetAuthorization", parameters: [("hash", String(describing: hash))]), 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 resetNotifySettings() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-612493497) - - return (FunctionDescription(name: "account.resetNotifySettings", parameters: []), 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 resetPassword() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1828139493) - - return (FunctionDescription(name: "account.resetPassword", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.ResetPasswordResult? in - let reader = BufferReader(buffer) - var result: Api.account.ResetPasswordResult? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.ResetPasswordResult - } - return result - }) - } -} -public extension Api.functions.account { - static func resetWallPapers() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1153722364) - - return (FunctionDescription(name: "account.resetWallPapers", parameters: []), 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 resetWebAuthorization(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(755087855) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.resetWebAuthorization", parameters: [("hash", String(describing: hash))]), 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 resetWebAuthorizations() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1747789204) - - return (FunctionDescription(name: "account.resetWebAuthorizations", parameters: []), 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 saveAutoDownloadSettings(flags: Int32, settings: Api.AutoDownloadSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1995661875) - serializeInt32(flags, buffer: buffer, boxed: false) - settings.serialize(buffer, true) - return (FunctionDescription(name: "account.saveAutoDownloadSettings", parameters: [("flags", String(describing: flags)), ("settings", String(describing: settings))]), 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 saveAutoSaveSettings(flags: Int32, peer: Api.InputPeer?, settings: Api.AutoSaveSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-694451359) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 3) != 0 {peer!.serialize(buffer, true)} - settings.serialize(buffer, true) - return (FunctionDescription(name: "account.saveAutoSaveSettings", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("settings", String(describing: settings))]), 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() - buffer.appendInt32(1038768899) - id.serialize(buffer, true) - unsave.serialize(buffer, true) - return (FunctionDescription(name: "account.saveRingtone", parameters: [("id", String(describing: id)), ("unsave", String(describing: unsave))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.SavedRingtone? in - let reader = BufferReader(buffer) - var result: Api.account.SavedRingtone? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.SavedRingtone - } - return result - }) - } -} -public extension Api.functions.account { - static func saveSecureValue(value: Api.InputSecureValue, secureSecretId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1986010339) - value.serialize(buffer, true) - serializeInt64(secureSecretId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.saveSecureValue", parameters: [("value", String(describing: value)), ("secureSecretId", String(describing: secureSecretId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.SecureValue? in - let reader = BufferReader(buffer) - var result: Api.SecureValue? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.SecureValue - } - return result - }) - } -} -public extension Api.functions.account { - static func saveTheme(theme: Api.InputTheme, unsave: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-229175188) - theme.serialize(buffer, true) - unsave.serialize(buffer, true) - return (FunctionDescription(name: "account.saveTheme", parameters: [("theme", String(describing: theme)), ("unsave", String(describing: unsave))]), 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 saveWallPaper(wallpaper: Api.InputWallPaper, unsave: Api.Bool, settings: Api.WallPaperSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1817860919) - wallpaper.serialize(buffer, true) - unsave.serialize(buffer, true) - settings.serialize(buffer, true) - return (FunctionDescription(name: "account.saveWallPaper", parameters: [("wallpaper", String(describing: wallpaper)), ("unsave", String(describing: unsave)), ("settings", String(describing: settings))]), 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 sendChangePhoneCode(phoneNumber: String, settings: Api.CodeSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-2108208411) - serializeString(phoneNumber, buffer: buffer, boxed: false) - settings.serialize(buffer, true) - return (FunctionDescription(name: "account.sendChangePhoneCode", parameters: [("phoneNumber", String(describing: phoneNumber)), ("settings", String(describing: settings))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in - let reader = BufferReader(buffer) - var result: Api.auth.SentCode? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.auth.SentCode - } - return result - }) - } -} -public extension Api.functions.account { - static func sendConfirmPhoneCode(hash: String, settings: Api.CodeSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(457157256) - serializeString(hash, buffer: buffer, boxed: false) - settings.serialize(buffer, true) - return (FunctionDescription(name: "account.sendConfirmPhoneCode", parameters: [("hash", String(describing: hash)), ("settings", String(describing: settings))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in - let reader = BufferReader(buffer) - var result: Api.auth.SentCode? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.auth.SentCode - } - return result - }) - } -} -public extension Api.functions.account { - static func sendVerifyEmailCode(purpose: Api.EmailVerifyPurpose, email: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1730136133) - purpose.serialize(buffer, true) - serializeString(email, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.sendVerifyEmailCode", parameters: [("purpose", String(describing: purpose)), ("email", String(describing: email))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.SentEmailCode? in - let reader = BufferReader(buffer) - var result: Api.account.SentEmailCode? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.SentEmailCode - } - return result - }) - } -} -public extension Api.functions.account { - static func sendVerifyPhoneCode(phoneNumber: String, settings: Api.CodeSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1516022023) - serializeString(phoneNumber, buffer: buffer, boxed: false) - settings.serialize(buffer, true) - return (FunctionDescription(name: "account.sendVerifyPhoneCode", parameters: [("phoneNumber", String(describing: phoneNumber)), ("settings", String(describing: settings))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in - let reader = BufferReader(buffer) - var result: Api.auth.SentCode? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.auth.SentCode - } - return result - }) - } -} -public extension Api.functions.account { - static func setAccountTTL(ttl: Api.AccountDaysTTL) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(608323678) - ttl.serialize(buffer, true) - return (FunctionDescription(name: "account.setAccountTTL", parameters: [("ttl", String(describing: ttl))]), 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 setAuthorizationTTL(authorizationTtlDays: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1081501024) - serializeInt32(authorizationTtlDays, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.setAuthorizationTTL", parameters: [("authorizationTtlDays", String(describing: authorizationTtlDays))]), 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 setContactSignUpNotification(silent: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-806076575) - silent.serialize(buffer, true) - return (FunctionDescription(name: "account.setContactSignUpNotification", parameters: [("silent", String(describing: silent))]), 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 setContentSettings(flags: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1250643605) - serializeInt32(flags, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.setContentSettings", parameters: [("flags", String(describing: flags))]), 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 setGlobalPrivacySettings(settings: Api.GlobalPrivacySettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(517647042) - settings.serialize(buffer, true) - return (FunctionDescription(name: "account.setGlobalPrivacySettings", parameters: [("settings", String(describing: settings))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.GlobalPrivacySettings? in - let reader = BufferReader(buffer) - var result: Api.GlobalPrivacySettings? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.GlobalPrivacySettings - } - return result - }) - } -} -public extension Api.functions.account { - static func setPrivacy(key: Api.InputPrivacyKey, rules: [Api.InputPrivacyRule]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-906486552) - key.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(rules.count)) - for item in rules { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "account.setPrivacy", parameters: [("key", String(describing: key)), ("rules", String(describing: rules))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.PrivacyRules? in - let reader = BufferReader(buffer) - var result: Api.account.PrivacyRules? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.PrivacyRules - } - return result - }) - } -} -public extension Api.functions.account { - static func toggleUsername(username: String, active: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1490465654) - serializeString(username, buffer: buffer, boxed: false) - active.serialize(buffer, true) - return (FunctionDescription(name: "account.toggleUsername", parameters: [("username", String(describing: username)), ("active", String(describing: active))]), 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 unregisterDevice(tokenType: Int32, token: String, otherUids: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1779249670) - serializeInt32(tokenType, buffer: buffer, boxed: false) - serializeString(token, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(otherUids.count)) - for item in otherUids { - serializeInt64(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "account.unregisterDevice", parameters: [("tokenType", String(describing: tokenType)), ("token", String(describing: token)), ("otherUids", String(describing: otherUids))]), 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 updateColor(flags: Int32, color: Int32, backgroundEmojiId: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1610494909) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(color, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt64(backgroundEmojiId!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "account.updateColor", parameters: [("flags", String(describing: flags)), ("color", String(describing: color)), ("backgroundEmojiId", String(describing: backgroundEmojiId))]), 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 updateDeviceLocked(period: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(954152242) - serializeInt32(period, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.updateDeviceLocked", parameters: [("period", String(describing: period))]), 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 updateEmojiStatus(emojiStatus: Api.EmojiStatus) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-70001045) - emojiStatus.serialize(buffer, true) - return (FunctionDescription(name: "account.updateEmojiStatus", parameters: [("emojiStatus", String(describing: emojiStatus))]), 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 updateNotifySettings(peer: Api.InputNotifyPeer, settings: Api.InputPeerNotifySettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-2067899501) - peer.serialize(buffer, true) - settings.serialize(buffer, true) - return (FunctionDescription(name: "account.updateNotifySettings", parameters: [("peer", String(describing: peer)), ("settings", String(describing: settings))]), 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 updatePasswordSettings(password: Api.InputCheckPasswordSRP, newSettings: Api.account.PasswordInputSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1516564433) - password.serialize(buffer, true) - newSettings.serialize(buffer, true) - return (FunctionDescription(name: "account.updatePasswordSettings", parameters: [("password", String(describing: password)), ("newSettings", String(describing: newSettings))]), 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 updateProfile(flags: Int32, firstName: String?, lastName: String?, about: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2018596725) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(firstName!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeString(lastName!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(about!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "account.updateProfile", parameters: [("flags", String(describing: flags)), ("firstName", String(describing: firstName)), ("lastName", String(describing: lastName)), ("about", String(describing: about))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.User? in - let reader = BufferReader(buffer) - var result: Api.User? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.User - } - return result - }) - } -} -public extension Api.functions.account { - static func updateStatus(offline: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1713919532) - offline.serialize(buffer, true) - return (FunctionDescription(name: "account.updateStatus", parameters: [("offline", String(describing: offline))]), 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 updateTheme(flags: Int32, format: String, theme: Api.InputTheme, slug: String?, title: String?, document: Api.InputDocument?, settings: [Api.InputThemeSettings]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(737414348) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(format, buffer: buffer, boxed: false) - theme.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {serializeString(slug!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {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) - }} - return (FunctionDescription(name: "account.updateTheme", parameters: [("flags", String(describing: flags)), ("format", String(describing: format)), ("theme", String(describing: theme)), ("slug", String(describing: slug)), ("title", String(describing: title)), ("document", String(describing: document)), ("settings", String(describing: settings))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Theme? in - let reader = BufferReader(buffer) - var result: Api.Theme? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Theme - } - return result - }) - } -} -public extension Api.functions.account { - static func updateUsername(username: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1040964988) - serializeString(username, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.updateUsername", parameters: [("username", String(describing: username))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.User? in - let reader = BufferReader(buffer) - var result: Api.User? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.User - } - return result - }) - } -} -public extension Api.functions.account { - static func uploadRingtone(file: Api.InputFile, fileName: String, mimeType: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-2095414366) - file.serialize(buffer, true) - serializeString(fileName, buffer: buffer, boxed: false) - serializeString(mimeType, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.uploadRingtone", parameters: [("file", String(describing: file)), ("fileName", String(describing: fileName)), ("mimeType", String(describing: mimeType))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Document? in - let reader = BufferReader(buffer) - var result: Api.Document? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Document - } - return result - }) - } -} -public extension Api.functions.account { - static func uploadTheme(flags: Int32, file: Api.InputFile, thumb: Api.InputFile?, fileName: String, mimeType: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(473805619) - serializeInt32(flags, buffer: buffer, boxed: false) - file.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {thumb!.serialize(buffer, true)} - serializeString(fileName, buffer: buffer, boxed: false) - serializeString(mimeType, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.uploadTheme", parameters: [("flags", String(describing: flags)), ("file", String(describing: file)), ("thumb", String(describing: thumb)), ("fileName", String(describing: fileName)), ("mimeType", String(describing: mimeType))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Document? in - let reader = BufferReader(buffer) - var result: Api.Document? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Document - } - return result - }) - } -} -public extension Api.functions.account { - static func uploadWallPaper(flags: Int32, file: Api.InputFile, mimeType: String, settings: Api.WallPaperSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-476410109) - serializeInt32(flags, buffer: buffer, boxed: false) - file.serialize(buffer, true) - serializeString(mimeType, buffer: buffer, boxed: false) - settings.serialize(buffer, true) - return (FunctionDescription(name: "account.uploadWallPaper", parameters: [("flags", String(describing: flags)), ("file", String(describing: file)), ("mimeType", String(describing: mimeType)), ("settings", String(describing: settings))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.WallPaper? in - let reader = BufferReader(buffer) - var result: Api.WallPaper? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.WallPaper - } - return result - }) - } -} -public extension Api.functions.account { - static func verifyEmail(purpose: Api.EmailVerifyPurpose, verification: Api.EmailVerification) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(53322959) - purpose.serialize(buffer, true) - verification.serialize(buffer, true) - return (FunctionDescription(name: "account.verifyEmail", parameters: [("purpose", String(describing: purpose)), ("verification", String(describing: verification))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.EmailVerified? in - let reader = BufferReader(buffer) - var result: Api.account.EmailVerified? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.EmailVerified - } - return result - }) - } -} -public extension Api.functions.account { - static func verifyPhone(phoneNumber: String, phoneCodeHash: String, phoneCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1305716726) - serializeString(phoneNumber, buffer: buffer, boxed: false) - serializeString(phoneCodeHash, buffer: buffer, boxed: false) - serializeString(phoneCode, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.verifyPhone", parameters: [("phoneNumber", String(describing: phoneNumber)), ("phoneCodeHash", String(describing: phoneCodeHash)), ("phoneCode", String(describing: phoneCode))]), 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.auth { - static func acceptLoginToken(token: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-392909491) - serializeBytes(token, buffer: buffer, boxed: false) - return (FunctionDescription(name: "auth.acceptLoginToken", parameters: [("token", String(describing: token))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Authorization? in - let reader = BufferReader(buffer) - var result: Api.Authorization? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Authorization - } - return result - }) - } -} -public extension Api.functions.auth { - static func bindTempAuthKey(permAuthKeyId: Int64, nonce: Int64, expiresAt: Int32, encryptedMessage: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-841733627) - serializeInt64(permAuthKeyId, buffer: buffer, boxed: false) - serializeInt64(nonce, buffer: buffer, boxed: false) - serializeInt32(expiresAt, buffer: buffer, boxed: false) - serializeBytes(encryptedMessage, buffer: buffer, boxed: false) - return (FunctionDescription(name: "auth.bindTempAuthKey", parameters: [("permAuthKeyId", String(describing: permAuthKeyId)), ("nonce", String(describing: nonce)), ("expiresAt", String(describing: expiresAt)), ("encryptedMessage", String(describing: encryptedMessage))]), 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.auth { - static func cancelCode(phoneNumber: String, phoneCodeHash: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(520357240) - serializeString(phoneNumber, buffer: buffer, boxed: false) - serializeString(phoneCodeHash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "auth.cancelCode", parameters: [("phoneNumber", String(describing: phoneNumber)), ("phoneCodeHash", String(describing: phoneCodeHash))]), 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.auth { - static func checkPassword(password: Api.InputCheckPasswordSRP) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-779399914) - password.serialize(buffer, true) - return (FunctionDescription(name: "auth.checkPassword", parameters: [("password", String(describing: password))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.Authorization? in - let reader = BufferReader(buffer) - var result: Api.auth.Authorization? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.auth.Authorization - } - return result - }) - } -} -public extension Api.functions.auth { - static func checkRecoveryPassword(code: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(221691769) - serializeString(code, buffer: buffer, boxed: false) - return (FunctionDescription(name: "auth.checkRecoveryPassword", parameters: [("code", String(describing: code))]), 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.auth { - static func dropTempAuthKeys(exceptAuthKeys: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1907842680) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(exceptAuthKeys.count)) - for item in exceptAuthKeys { - serializeInt64(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "auth.dropTempAuthKeys", parameters: [("exceptAuthKeys", String(describing: exceptAuthKeys))]), 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.auth { - static func exportAuthorization(dcId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-440401971) - serializeInt32(dcId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "auth.exportAuthorization", parameters: [("dcId", String(describing: dcId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.ExportedAuthorization? in - let reader = BufferReader(buffer) - var result: Api.auth.ExportedAuthorization? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.auth.ExportedAuthorization - } - return result - }) - } -} -public extension Api.functions.auth { - static func exportLoginToken(apiId: Int32, apiHash: String, exceptIds: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1210022402) - serializeInt32(apiId, buffer: buffer, boxed: false) - serializeString(apiHash, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(exceptIds.count)) - for item in exceptIds { - serializeInt64(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "auth.exportLoginToken", parameters: [("apiId", String(describing: apiId)), ("apiHash", String(describing: apiHash)), ("exceptIds", String(describing: exceptIds))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.LoginToken? in - let reader = BufferReader(buffer) - var result: Api.auth.LoginToken? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.auth.LoginToken - } - return result - }) - } -} -public extension Api.functions.auth { - static func importAuthorization(id: Int64, bytes: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1518699091) - serializeInt64(id, buffer: buffer, boxed: false) - serializeBytes(bytes, buffer: buffer, boxed: false) - return (FunctionDescription(name: "auth.importAuthorization", parameters: [("id", String(describing: id)), ("bytes", String(describing: bytes))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.Authorization? in - let reader = BufferReader(buffer) - var result: Api.auth.Authorization? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.auth.Authorization - } - return result - }) - } -} -public extension Api.functions.auth { - static func importBotAuthorization(flags: Int32, apiId: Int32, apiHash: String, botAuthToken: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1738800940) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(apiId, buffer: buffer, boxed: false) - serializeString(apiHash, buffer: buffer, boxed: false) - serializeString(botAuthToken, buffer: buffer, boxed: false) - return (FunctionDescription(name: "auth.importBotAuthorization", parameters: [("flags", String(describing: flags)), ("apiId", String(describing: apiId)), ("apiHash", String(describing: apiHash)), ("botAuthToken", String(describing: botAuthToken))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.Authorization? in - let reader = BufferReader(buffer) - var result: Api.auth.Authorization? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.auth.Authorization - } - return result - }) - } -} -public extension Api.functions.auth { - static func importLoginToken(token: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1783866140) - serializeBytes(token, buffer: buffer, boxed: false) - return (FunctionDescription(name: "auth.importLoginToken", parameters: [("token", String(describing: token))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.LoginToken? in - let reader = BufferReader(buffer) - var result: Api.auth.LoginToken? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.auth.LoginToken - } - return result - }) - } -} -public extension Api.functions.auth { - static func importWebTokenAuthorization(apiId: Int32, apiHash: String, webAuthToken: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(767062953) - serializeInt32(apiId, buffer: buffer, boxed: false) - serializeString(apiHash, buffer: buffer, boxed: false) - serializeString(webAuthToken, buffer: buffer, boxed: false) - return (FunctionDescription(name: "auth.importWebTokenAuthorization", parameters: [("apiId", String(describing: apiId)), ("apiHash", String(describing: apiHash)), ("webAuthToken", String(describing: webAuthToken))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.Authorization? in - let reader = BufferReader(buffer) - var result: Api.auth.Authorization? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.auth.Authorization - } - return result - }) - } -} -public extension Api.functions.auth { - static func logOut() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1047706137) - - return (FunctionDescription(name: "auth.logOut", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.LoggedOut? in - let reader = BufferReader(buffer) - var result: Api.auth.LoggedOut? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.auth.LoggedOut - } - return result - }) - } -} -public extension Api.functions.auth { - static func recoverPassword(flags: Int32, code: String, newSettings: Api.account.PasswordInputSettings?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(923364464) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(code, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {newSettings!.serialize(buffer, true)} - return (FunctionDescription(name: "auth.recoverPassword", parameters: [("flags", String(describing: flags)), ("code", String(describing: code)), ("newSettings", String(describing: newSettings))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.Authorization? in - let reader = BufferReader(buffer) - var result: Api.auth.Authorization? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.auth.Authorization - } - return result - }) - } -} -public extension Api.functions.auth { - static func requestFirebaseSms(flags: Int32, phoneNumber: String, phoneCodeHash: String, safetyNetToken: String?, iosPushSecret: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1991881904) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(phoneNumber, buffer: buffer, boxed: false) - serializeString(phoneCodeHash, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(safetyNetToken!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeString(iosPushSecret!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "auth.requestFirebaseSms", parameters: [("flags", String(describing: flags)), ("phoneNumber", String(describing: phoneNumber)), ("phoneCodeHash", String(describing: phoneCodeHash)), ("safetyNetToken", String(describing: safetyNetToken)), ("iosPushSecret", String(describing: iosPushSecret))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - 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.auth { - static func requestPasswordRecovery() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-661144474) - - return (FunctionDescription(name: "auth.requestPasswordRecovery", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.PasswordRecovery? in - let reader = BufferReader(buffer) - var result: Api.auth.PasswordRecovery? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.auth.PasswordRecovery - } - return result - }) - } -} -public extension Api.functions.auth { - static func resendCode(phoneNumber: String, phoneCodeHash: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1056025023) - serializeString(phoneNumber, buffer: buffer, boxed: false) - serializeString(phoneCodeHash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "auth.resendCode", parameters: [("phoneNumber", String(describing: phoneNumber)), ("phoneCodeHash", String(describing: phoneCodeHash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in - let reader = BufferReader(buffer) - var result: Api.auth.SentCode? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.auth.SentCode - } - return result - }) - } -} -public extension Api.functions.auth { - static func resetAuthorizations() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1616179942) - - return (FunctionDescription(name: "auth.resetAuthorizations", parameters: []), 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.auth { - static func resetLoginEmail(phoneNumber: String, phoneCodeHash: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2123760019) - serializeString(phoneNumber, buffer: buffer, boxed: false) - serializeString(phoneCodeHash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "auth.resetLoginEmail", parameters: [("phoneNumber", String(describing: phoneNumber)), ("phoneCodeHash", String(describing: phoneCodeHash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in - let reader = BufferReader(buffer) - var result: Api.auth.SentCode? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.auth.SentCode - } - return result - }) - } -} -public extension Api.functions.auth { - static func sendCode(phoneNumber: String, apiId: Int32, apiHash: String, settings: Api.CodeSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1502141361) - serializeString(phoneNumber, buffer: buffer, boxed: false) - serializeInt32(apiId, buffer: buffer, boxed: false) - serializeString(apiHash, buffer: buffer, boxed: false) - settings.serialize(buffer, true) - return (FunctionDescription(name: "auth.sendCode", parameters: [("phoneNumber", String(describing: phoneNumber)), ("apiId", String(describing: apiId)), ("apiHash", String(describing: apiHash)), ("settings", String(describing: settings))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in - let reader = BufferReader(buffer) - var result: Api.auth.SentCode? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.auth.SentCode - } - return result - }) - } -} -public extension Api.functions.auth { - static func signIn(flags: Int32, phoneNumber: String, phoneCodeHash: String, phoneCode: String?, emailVerification: Api.EmailVerification?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1923962543) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(phoneNumber, buffer: buffer, boxed: false) - serializeString(phoneCodeHash, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(phoneCode!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {emailVerification!.serialize(buffer, true)} - return (FunctionDescription(name: "auth.signIn", parameters: [("flags", String(describing: flags)), ("phoneNumber", String(describing: phoneNumber)), ("phoneCodeHash", String(describing: phoneCodeHash)), ("phoneCode", String(describing: phoneCode)), ("emailVerification", String(describing: emailVerification))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.Authorization? in - let reader = BufferReader(buffer) - var result: Api.auth.Authorization? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.auth.Authorization - } - return result - }) - } -} -public extension Api.functions.auth { - static func signUp(phoneNumber: String, phoneCodeHash: String, firstName: String, lastName: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-2131827673) - serializeString(phoneNumber, buffer: buffer, boxed: false) - serializeString(phoneCodeHash, buffer: buffer, boxed: false) - serializeString(firstName, buffer: buffer, boxed: false) - serializeString(lastName, buffer: buffer, boxed: false) - return (FunctionDescription(name: "auth.signUp", parameters: [("phoneNumber", String(describing: phoneNumber)), ("phoneCodeHash", String(describing: phoneCodeHash)), ("firstName", String(describing: firstName)), ("lastName", String(describing: lastName))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.Authorization? in - let reader = BufferReader(buffer) - var result: Api.auth.Authorization? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.auth.Authorization - } - return result - }) - } -} -public extension Api.functions.bots { - static func allowSendMessage(bot: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-248323089) - bot.serialize(buffer, true) - return (FunctionDescription(name: "bots.allowSendMessage", parameters: [("bot", String(describing: bot))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.bots { - static func answerWebhookJSONQuery(queryId: Int64, data: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-434028723) - serializeInt64(queryId, buffer: buffer, boxed: false) - data.serialize(buffer, true) - return (FunctionDescription(name: "bots.answerWebhookJSONQuery", parameters: [("queryId", String(describing: queryId)), ("data", String(describing: data))]), 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.bots { - static func canSendMessage(bot: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(324662502) - bot.serialize(buffer, true) - return (FunctionDescription(name: "bots.canSendMessage", parameters: [("bot", String(describing: bot))]), 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.bots { - static func getBotCommands(scope: Api.BotCommandScope, langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.BotCommand]>) { - let buffer = Buffer() - buffer.appendInt32(-481554986) - scope.serialize(buffer, true) - serializeString(langCode, buffer: buffer, boxed: false) - return (FunctionDescription(name: "bots.getBotCommands", parameters: [("scope", String(describing: scope)), ("langCode", String(describing: langCode))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.BotCommand]? in - let reader = BufferReader(buffer) - var result: [Api.BotCommand]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.BotCommand.self) - } - return result - }) - } -} -public extension Api.functions.bots { - static func getBotInfo(flags: Int32, bot: Api.InputUser?, langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-589753091) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {bot!.serialize(buffer, true)} - serializeString(langCode, buffer: buffer, boxed: false) - return (FunctionDescription(name: "bots.getBotInfo", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("langCode", String(describing: langCode))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.bots.BotInfo? in - let reader = BufferReader(buffer) - var result: Api.bots.BotInfo? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.bots.BotInfo - } - return result - }) - } -} -public extension Api.functions.bots { - static func getBotMenuButton(userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1671369944) - userId.serialize(buffer, true) - return (FunctionDescription(name: "bots.getBotMenuButton", parameters: [("userId", String(describing: userId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.BotMenuButton? in - let reader = BufferReader(buffer) - var result: Api.BotMenuButton? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.BotMenuButton - } - return result - }) - } -} -public extension Api.functions.bots { - static func invokeWebViewCustomMethod(bot: Api.InputUser, customMethod: String, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(142591463) - bot.serialize(buffer, true) - serializeString(customMethod, buffer: buffer, boxed: false) - params.serialize(buffer, true) - return (FunctionDescription(name: "bots.invokeWebViewCustomMethod", parameters: [("bot", String(describing: bot)), ("customMethod", String(describing: customMethod)), ("params", String(describing: params))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.DataJSON? in - let reader = BufferReader(buffer) - var result: Api.DataJSON? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.DataJSON - } - return result - }) - } -} -public extension Api.functions.bots { - static func reorderUsernames(bot: Api.InputUser, order: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1760972350) - bot.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(order.count)) - for item in order { - serializeString(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "bots.reorderUsernames", parameters: [("bot", String(describing: bot)), ("order", String(describing: order))]), 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.bots { - static func resetBotCommands(scope: Api.BotCommandScope, langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1032708345) - scope.serialize(buffer, true) - serializeString(langCode, buffer: buffer, boxed: false) - return (FunctionDescription(name: "bots.resetBotCommands", parameters: [("scope", String(describing: scope)), ("langCode", String(describing: langCode))]), 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.bots { - static func sendCustomRequest(customMethod: String, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1440257555) - serializeString(customMethod, buffer: buffer, boxed: false) - params.serialize(buffer, true) - return (FunctionDescription(name: "bots.sendCustomRequest", parameters: [("customMethod", String(describing: customMethod)), ("params", String(describing: params))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.DataJSON? in - let reader = BufferReader(buffer) - var result: Api.DataJSON? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.DataJSON - } - return result - }) - } -} -public extension Api.functions.bots { - static func setBotBroadcastDefaultAdminRights(adminRights: Api.ChatAdminRights) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2021942497) - adminRights.serialize(buffer, true) - return (FunctionDescription(name: "bots.setBotBroadcastDefaultAdminRights", parameters: [("adminRights", String(describing: adminRights))]), 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.bots { - static func setBotCommands(scope: Api.BotCommandScope, langCode: String, commands: [Api.BotCommand]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(85399130) - scope.serialize(buffer, true) - serializeString(langCode, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(commands.count)) - for item in commands { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "bots.setBotCommands", parameters: [("scope", String(describing: scope)), ("langCode", String(describing: langCode)), ("commands", String(describing: commands))]), 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.bots { - static func setBotGroupDefaultAdminRights(adminRights: Api.ChatAdminRights) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1839281686) - adminRights.serialize(buffer, true) - return (FunctionDescription(name: "bots.setBotGroupDefaultAdminRights", parameters: [("adminRights", String(describing: adminRights))]), 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.bots { - static func setBotInfo(flags: Int32, bot: Api.InputUser?, langCode: String, name: String?, about: String?, description: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(282013987) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {bot!.serialize(buffer, true)} - serializeString(langCode, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 3) != 0 {serializeString(name!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 0) != 0 {serializeString(about!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeString(description!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "bots.setBotInfo", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("langCode", String(describing: langCode)), ("name", String(describing: name)), ("about", String(describing: about)), ("description", String(describing: description))]), 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.bots { - static func setBotMenuButton(userId: Api.InputUser, button: Api.BotMenuButton) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1157944655) - userId.serialize(buffer, true) - button.serialize(buffer, true) - return (FunctionDescription(name: "bots.setBotMenuButton", parameters: [("userId", String(describing: userId)), ("button", String(describing: button))]), 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.bots { - static func toggleUsername(bot: Api.InputUser, username: String, active: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(87861619) - bot.serialize(buffer, true) - serializeString(username, buffer: buffer, boxed: false) - active.serialize(buffer, true) - return (FunctionDescription(name: "bots.toggleUsername", parameters: [("bot", String(describing: bot)), ("username", String(describing: username)), ("active", String(describing: active))]), 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 checkUsername(channel: Api.InputChannel, username: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(283557164) - channel.serialize(buffer, true) - serializeString(username, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.checkUsername", parameters: [("channel", String(describing: channel)), ("username", String(describing: username))]), 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 clickSponsoredMessage(channel: Api.InputChannel, randomId: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(414170259) - channel.serialize(buffer, true) - serializeBytes(randomId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.clickSponsoredMessage", parameters: [("channel", String(describing: channel)), ("randomId", String(describing: randomId))]), 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 convertToGigagroup(channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(187239529) - channel.serialize(buffer, true) - return (FunctionDescription(name: "channels.convertToGigagroup", parameters: [("channel", String(describing: channel))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func createChannel(flags: Int32, title: String, about: String, geoPoint: Api.InputGeoPoint?, address: String?, ttlPeriod: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1862244601) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - serializeString(about, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {geoPoint!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(address!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "channels.createChannel", parameters: [("flags", String(describing: flags)), ("title", String(describing: title)), ("about", String(describing: about)), ("geoPoint", String(describing: geoPoint)), ("address", String(describing: address)), ("ttlPeriod", String(describing: ttlPeriod))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func createForumTopic(flags: Int32, channel: Api.InputChannel, title: String, iconColor: Int32?, iconEmojiId: Int64?, randomId: Int64, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-200539612) - serializeInt32(flags, buffer: buffer, boxed: false) - channel.serialize(buffer, true) - serializeString(title, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(iconColor!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {serializeInt64(iconEmojiId!, buffer: buffer, boxed: false)} - serializeInt64(randomId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {sendAs!.serialize(buffer, true)} - return (FunctionDescription(name: "channels.createForumTopic", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("title", String(describing: title)), ("iconColor", String(describing: iconColor)), ("iconEmojiId", String(describing: iconEmojiId)), ("randomId", String(describing: randomId)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func deactivateAllUsernames(channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(170155475) - channel.serialize(buffer, true) - return (FunctionDescription(name: "channels.deactivateAllUsernames", parameters: [("channel", String(describing: channel))]), 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 deleteChannel(channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1072619549) - channel.serialize(buffer, true) - return (FunctionDescription(name: "channels.deleteChannel", parameters: [("channel", String(describing: channel))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func deleteHistory(flags: Int32, channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1683319225) - serializeInt32(flags, buffer: buffer, boxed: false) - channel.serialize(buffer, true) - serializeInt32(maxId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.deleteHistory", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("maxId", String(describing: maxId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func deleteMessages(channel: Api.InputChannel, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-2067661490) - channel.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "channels.deleteMessages", parameters: [("channel", String(describing: channel)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedMessages? in - let reader = BufferReader(buffer) - var result: Api.messages.AffectedMessages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.AffectedMessages - } - return result - }) - } -} -public extension Api.functions.channels { - static func deleteParticipantHistory(channel: Api.InputChannel, participant: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(913655003) - channel.serialize(buffer, true) - participant.serialize(buffer, true) - return (FunctionDescription(name: "channels.deleteParticipantHistory", parameters: [("channel", String(describing: channel)), ("participant", String(describing: participant))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedHistory? in - let reader = BufferReader(buffer) - var result: Api.messages.AffectedHistory? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.AffectedHistory - } - return result - }) - } -} -public extension Api.functions.channels { - static func deleteTopicHistory(channel: Api.InputChannel, topMsgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(876830509) - channel.serialize(buffer, true) - serializeInt32(topMsgId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.deleteTopicHistory", parameters: [("channel", String(describing: channel)), ("topMsgId", String(describing: topMsgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedHistory? in - let reader = BufferReader(buffer) - var result: Api.messages.AffectedHistory? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.AffectedHistory - } - return result - }) - } -} -public extension Api.functions.channels { - static func editAdmin(channel: Api.InputChannel, userId: Api.InputUser, adminRights: Api.ChatAdminRights, rank: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-751007486) - channel.serialize(buffer, true) - userId.serialize(buffer, true) - adminRights.serialize(buffer, true) - serializeString(rank, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.editAdmin", parameters: [("channel", String(describing: channel)), ("userId", String(describing: userId)), ("adminRights", String(describing: adminRights)), ("rank", String(describing: rank))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func editBanned(channel: Api.InputChannel, participant: Api.InputPeer, bannedRights: Api.ChatBannedRights) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1763259007) - channel.serialize(buffer, true) - participant.serialize(buffer, true) - bannedRights.serialize(buffer, true) - return (FunctionDescription(name: "channels.editBanned", parameters: [("channel", String(describing: channel)), ("participant", String(describing: participant)), ("bannedRights", String(describing: bannedRights))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func editCreator(channel: Api.InputChannel, userId: Api.InputUser, password: Api.InputCheckPasswordSRP) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1892102881) - channel.serialize(buffer, true) - userId.serialize(buffer, true) - password.serialize(buffer, true) - return (FunctionDescription(name: "channels.editCreator", parameters: [("channel", String(describing: channel)), ("userId", String(describing: userId)), ("password", String(describing: password))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func editForumTopic(flags: Int32, channel: Api.InputChannel, topicId: Int32, title: String?, iconEmojiId: Int64?, closed: Api.Bool?, hidden: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-186670715) - serializeInt32(flags, buffer: buffer, boxed: false) - channel.serialize(buffer, true) - serializeInt32(topicId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeInt64(iconEmojiId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {closed!.serialize(buffer, true)} - if Int(flags) & Int(1 << 3) != 0 {hidden!.serialize(buffer, true)} - return (FunctionDescription(name: "channels.editForumTopic", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("topicId", String(describing: topicId)), ("title", String(describing: title)), ("iconEmojiId", String(describing: iconEmojiId)), ("closed", String(describing: closed)), ("hidden", String(describing: hidden))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func editLocation(channel: Api.InputChannel, geoPoint: Api.InputGeoPoint, address: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1491484525) - channel.serialize(buffer, true) - geoPoint.serialize(buffer, true) - serializeString(address, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.editLocation", parameters: [("channel", String(describing: channel)), ("geoPoint", String(describing: geoPoint)), ("address", String(describing: address))]), 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 editPhoto(channel: Api.InputChannel, photo: Api.InputChatPhoto) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-248621111) - channel.serialize(buffer, true) - photo.serialize(buffer, true) - return (FunctionDescription(name: "channels.editPhoto", parameters: [("channel", String(describing: channel)), ("photo", String(describing: photo))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func editTitle(channel: Api.InputChannel, title: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1450044624) - channel.serialize(buffer, true) - serializeString(title, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.editTitle", parameters: [("channel", String(describing: channel)), ("title", String(describing: title))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func exportMessageLink(flags: Int32, channel: Api.InputChannel, id: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-432034325) - serializeInt32(flags, buffer: buffer, boxed: false) - channel.serialize(buffer, true) - serializeInt32(id, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.exportMessageLink", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedMessageLink? in - let reader = BufferReader(buffer) - var result: Api.ExportedMessageLink? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.ExportedMessageLink - } - return result - }) - } -} -public extension Api.functions.channels { - static func getAdminLog(flags: Int32, channel: Api.InputChannel, q: String, eventsFilter: Api.ChannelAdminLogEventsFilter?, admins: [Api.InputUser]?, maxId: Int64, minId: Int64, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(870184064) - serializeInt32(flags, buffer: buffer, boxed: false) - channel.serialize(buffer, true) - serializeString(q, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {eventsFilter!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(admins!.count)) - for item in admins! { - item.serialize(buffer, true) - }} - serializeInt64(maxId, buffer: buffer, boxed: false) - serializeInt64(minId, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.getAdminLog", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("q", String(describing: q)), ("eventsFilter", String(describing: eventsFilter)), ("admins", String(describing: admins)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.channels.AdminLogResults? in - let reader = BufferReader(buffer) - var result: Api.channels.AdminLogResults? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.channels.AdminLogResults - } - return result - }) - } -} -public extension Api.functions.channels { - static func getAdminedPublicChannels(flags: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-122669393) - serializeInt32(flags, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.getAdminedPublicChannels", parameters: [("flags", String(describing: flags))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Chats? in - let reader = BufferReader(buffer) - var result: Api.messages.Chats? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Chats - } - return result - }) - } -} -public extension Api.functions.channels { - static func getChannels(id: [Api.InputChannel]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(176122811) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "channels.getChannels", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Chats? in - let reader = BufferReader(buffer) - var result: Api.messages.Chats? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Chats - } - return result - }) - } -} -public extension Api.functions.channels { - static func getForumTopics(flags: Int32, channel: Api.InputChannel, q: String?, offsetDate: Int32, offsetId: Int32, offsetTopic: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(233136337) - serializeInt32(flags, buffer: buffer, boxed: false) - channel.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {serializeString(q!, buffer: buffer, boxed: false)} - serializeInt32(offsetDate, buffer: buffer, boxed: false) - serializeInt32(offsetId, buffer: buffer, boxed: false) - serializeInt32(offsetTopic, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.getForumTopics", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("q", String(describing: q)), ("offsetDate", String(describing: offsetDate)), ("offsetId", String(describing: offsetId)), ("offsetTopic", String(describing: offsetTopic)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ForumTopics? in - let reader = BufferReader(buffer) - var result: Api.messages.ForumTopics? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.ForumTopics - } - return result - }) - } -} -public extension Api.functions.channels { - static func getForumTopicsByID(channel: Api.InputChannel, topics: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1333584199) - channel.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(topics.count)) - for item in topics { - serializeInt32(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "channels.getForumTopicsByID", parameters: [("channel", String(describing: channel)), ("topics", String(describing: topics))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ForumTopics? in - let reader = BufferReader(buffer) - var result: Api.messages.ForumTopics? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.ForumTopics - } - return result - }) - } -} -public extension Api.functions.channels { - static func getFullChannel(channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(141781513) - channel.serialize(buffer, true) - return (FunctionDescription(name: "channels.getFullChannel", parameters: [("channel", String(describing: channel))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ChatFull? in - let reader = BufferReader(buffer) - var result: Api.messages.ChatFull? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.ChatFull - } - return result - }) - } -} -public extension Api.functions.channels { - static func getGroupsForDiscussion() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-170208392) - - return (FunctionDescription(name: "channels.getGroupsForDiscussion", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Chats? in - let reader = BufferReader(buffer) - var result: Api.messages.Chats? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Chats - } - return result - }) - } -} -public extension Api.functions.channels { - static func getInactiveChannels() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(300429806) - - return (FunctionDescription(name: "channels.getInactiveChannels", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.InactiveChats? in - let reader = BufferReader(buffer) - var result: Api.messages.InactiveChats? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.InactiveChats - } - return result - }) - } -} -public extension Api.functions.channels { - static func getLeftChannels(offset: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-2092831552) - serializeInt32(offset, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.getLeftChannels", parameters: [("offset", String(describing: offset))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Chats? in - let reader = BufferReader(buffer) - var result: Api.messages.Chats? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Chats - } - return result - }) - } -} -public extension Api.functions.channels { - static func getMessages(channel: Api.InputChannel, id: [Api.InputMessage]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1383294429) - channel.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "channels.getMessages", parameters: [("channel", String(describing: channel)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in - let reader = BufferReader(buffer) - var result: Api.messages.Messages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Messages - } - return result - }) - } -} -public extension Api.functions.channels { - static func getParticipant(channel: Api.InputChannel, participant: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1599378234) - channel.serialize(buffer, true) - participant.serialize(buffer, true) - return (FunctionDescription(name: "channels.getParticipant", parameters: [("channel", String(describing: channel)), ("participant", String(describing: participant))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.channels.ChannelParticipant? in - let reader = BufferReader(buffer) - var result: Api.channels.ChannelParticipant? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.channels.ChannelParticipant - } - return result - }) - } -} -public extension Api.functions.channels { - static func getParticipants(channel: Api.InputChannel, filter: Api.ChannelParticipantsFilter, offset: Int32, limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2010044880) - channel.serialize(buffer, true) - filter.serialize(buffer, true) - serializeInt32(offset, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.getParticipants", parameters: [("channel", String(describing: channel)), ("filter", String(describing: filter)), ("offset", String(describing: offset)), ("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.channels.ChannelParticipants? in - let reader = BufferReader(buffer) - var result: Api.channels.ChannelParticipants? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.channels.ChannelParticipants - } - return result - }) - } -} -public extension Api.functions.channels { - static func getSendAs(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(231174382) - peer.serialize(buffer, true) - return (FunctionDescription(name: "channels.getSendAs", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.channels.SendAsPeers? in - let reader = BufferReader(buffer) - var result: Api.channels.SendAsPeers? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.channels.SendAsPeers - } - return result - }) - } -} -public extension Api.functions.channels { - static func getSponsoredMessages(channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-333377601) - channel.serialize(buffer, true) - return (FunctionDescription(name: "channels.getSponsoredMessages", parameters: [("channel", String(describing: channel))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SponsoredMessages? in - let reader = BufferReader(buffer) - var result: Api.messages.SponsoredMessages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.SponsoredMessages - } - return result - }) - } -} -public extension Api.functions.channels { - static func inviteToChannel(channel: Api.InputChannel, users: [Api.InputUser]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(429865580) - channel.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "channels.inviteToChannel", parameters: [("channel", String(describing: channel)), ("users", String(describing: users))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func joinChannel(channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(615851205) - channel.serialize(buffer, true) - return (FunctionDescription(name: "channels.joinChannel", parameters: [("channel", String(describing: channel))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func leaveChannel(channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-130635115) - channel.serialize(buffer, true) - return (FunctionDescription(name: "channels.leaveChannel", parameters: [("channel", String(describing: channel))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-871347913) - channel.serialize(buffer, true) - serializeInt32(maxId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.readHistory", parameters: [("channel", String(describing: channel)), ("maxId", String(describing: maxId))]), 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 readMessageContents(channel: Api.InputChannel, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-357180360) - channel.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "channels.readMessageContents", parameters: [("channel", String(describing: channel)), ("id", String(describing: id))]), 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 reorderPinnedForumTopics(flags: Int32, channel: Api.InputChannel, order: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(693150095) - serializeInt32(flags, buffer: buffer, boxed: false) - channel.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(order.count)) - for item in order { - serializeInt32(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "channels.reorderPinnedForumTopics", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("order", String(describing: order))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func reorderUsernames(channel: Api.InputChannel, order: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1268978403) - channel.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(order.count)) - for item in order { - serializeString(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "channels.reorderUsernames", parameters: [("channel", String(describing: channel)), ("order", String(describing: order))]), 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 reportAntiSpamFalsePositive(channel: Api.InputChannel, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1471109485) - channel.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.reportAntiSpamFalsePositive", parameters: [("channel", String(describing: channel)), ("msgId", String(describing: msgId))]), 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 reportSpam(channel: Api.InputChannel, participant: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-196443371) - channel.serialize(buffer, true) - participant.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "channels.reportSpam", parameters: [("channel", String(describing: channel)), ("participant", String(describing: participant)), ("id", String(describing: id))]), 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 setDiscussionGroup(broadcast: Api.InputChannel, group: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1079520178) - broadcast.serialize(buffer, true) - group.serialize(buffer, true) - return (FunctionDescription(name: "channels.setDiscussionGroup", parameters: [("broadcast", String(describing: broadcast)), ("group", String(describing: group))]), 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() - buffer.appendInt32(-359881479) - channel.serialize(buffer, true) - stickerset.serialize(buffer, true) - return (FunctionDescription(name: "channels.setStickers", parameters: [("channel", String(describing: channel)), ("stickerset", String(describing: stickerset))]), 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 toggleAntiSpam(channel: Api.InputChannel, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1760814315) - channel.serialize(buffer, true) - enabled.serialize(buffer, true) - return (FunctionDescription(name: "channels.toggleAntiSpam", parameters: [("channel", String(describing: channel)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func toggleForum(channel: Api.InputChannel, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1540781271) - channel.serialize(buffer, true) - enabled.serialize(buffer, true) - return (FunctionDescription(name: "channels.toggleForum", parameters: [("channel", String(describing: channel)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func toggleJoinRequest(channel: Api.InputChannel, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1277789622) - channel.serialize(buffer, true) - enabled.serialize(buffer, true) - return (FunctionDescription(name: "channels.toggleJoinRequest", parameters: [("channel", String(describing: channel)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func toggleJoinToSend(channel: Api.InputChannel, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-456419968) - channel.serialize(buffer, true) - enabled.serialize(buffer, true) - return (FunctionDescription(name: "channels.toggleJoinToSend", parameters: [("channel", String(describing: channel)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func toggleParticipantsHidden(channel: Api.InputChannel, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1785624660) - channel.serialize(buffer, true) - enabled.serialize(buffer, true) - return (FunctionDescription(name: "channels.toggleParticipantsHidden", parameters: [("channel", String(describing: channel)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func togglePreHistoryHidden(channel: Api.InputChannel, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-356796084) - channel.serialize(buffer, true) - enabled.serialize(buffer, true) - return (FunctionDescription(name: "channels.togglePreHistoryHidden", parameters: [("channel", String(describing: channel)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func toggleSignatures(channel: Api.InputChannel, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(527021574) - channel.serialize(buffer, true) - enabled.serialize(buffer, true) - return (FunctionDescription(name: "channels.toggleSignatures", parameters: [("channel", String(describing: channel)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func toggleSlowMode(channel: Api.InputChannel, seconds: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-304832784) - channel.serialize(buffer, true) - serializeInt32(seconds, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.toggleSlowMode", parameters: [("channel", String(describing: channel)), ("seconds", String(describing: seconds))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func toggleUsername(channel: Api.InputChannel, username: String, active: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1358053637) - channel.serialize(buffer, true) - serializeString(username, buffer: buffer, boxed: false) - active.serialize(buffer, true) - return (FunctionDescription(name: "channels.toggleUsername", parameters: [("channel", String(describing: channel)), ("username", String(describing: username)), ("active", String(describing: active))]), 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 updateColor(flags: Int32, channel: Api.InputChannel, color: Int32, backgroundEmojiId: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1645879327) - serializeInt32(flags, buffer: buffer, boxed: false) - channel.serialize(buffer, true) - serializeInt32(color, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt64(backgroundEmojiId!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "channels.updateColor", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("color", String(describing: color)), ("backgroundEmojiId", String(describing: backgroundEmojiId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func updatePinnedForumTopic(channel: Api.InputChannel, topicId: Int32, pinned: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1814925350) - channel.serialize(buffer, true) - serializeInt32(topicId, buffer: buffer, boxed: false) - pinned.serialize(buffer, true) - return (FunctionDescription(name: "channels.updatePinnedForumTopic", parameters: [("channel", String(describing: channel)), ("topicId", String(describing: topicId)), ("pinned", String(describing: pinned))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.channels { - static func updateUsername(channel: Api.InputChannel, username: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(890549214) - channel.serialize(buffer, true) - serializeString(username, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.updateUsername", parameters: [("channel", String(describing: channel)), ("username", String(describing: username))]), 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 viewSponsoredMessage(channel: Api.InputChannel, randomId: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1095836780) - channel.serialize(buffer, true) - serializeBytes(randomId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.viewSponsoredMessage", parameters: [("channel", String(describing: channel)), ("randomId", String(describing: randomId))]), 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.chatlists { - static func checkChatlistInvite(slug: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1103171583) - serializeString(slug, buffer: buffer, boxed: false) - return (FunctionDescription(name: "chatlists.checkChatlistInvite", parameters: [("slug", String(describing: slug))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.chatlists.ChatlistInvite? in - let reader = BufferReader(buffer) - var result: Api.chatlists.ChatlistInvite? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.chatlists.ChatlistInvite - } - return result - }) - } -} -public extension Api.functions.chatlists { - static func deleteExportedInvite(chatlist: Api.InputChatlist, slug: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1906072670) - chatlist.serialize(buffer, true) - serializeString(slug, buffer: buffer, boxed: false) - return (FunctionDescription(name: "chatlists.deleteExportedInvite", parameters: [("chatlist", String(describing: chatlist)), ("slug", String(describing: slug))]), 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.chatlists { - static func editExportedInvite(flags: Int32, chatlist: Api.InputChatlist, slug: String, title: String?, peers: [Api.InputPeer]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1698543165) - serializeInt32(flags, buffer: buffer, boxed: false) - chatlist.serialize(buffer, true) - serializeString(slug, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeString(title!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(peers!.count)) - for item in peers! { - item.serialize(buffer, true) - }} - return (FunctionDescription(name: "chatlists.editExportedInvite", parameters: [("flags", String(describing: flags)), ("chatlist", String(describing: chatlist)), ("slug", String(describing: slug)), ("title", String(describing: title)), ("peers", String(describing: peers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedChatlistInvite? in - let reader = BufferReader(buffer) - var result: Api.ExportedChatlistInvite? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.ExportedChatlistInvite - } - return result - }) - } -} -public extension Api.functions.chatlists { - static func exportChatlistInvite(chatlist: Api.InputChatlist, title: String, peers: [Api.InputPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-2072885362) - chatlist.serialize(buffer, true) - serializeString(title, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(peers.count)) - for item in peers { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "chatlists.exportChatlistInvite", parameters: [("chatlist", String(describing: chatlist)), ("title", String(describing: title)), ("peers", String(describing: peers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.chatlists.ExportedChatlistInvite? in - let reader = BufferReader(buffer) - var result: Api.chatlists.ExportedChatlistInvite? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.chatlists.ExportedChatlistInvite - } - return result - }) - } -} -public extension Api.functions.chatlists { - static func getChatlistUpdates(chatlist: Api.InputChatlist) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1992190687) - chatlist.serialize(buffer, true) - return (FunctionDescription(name: "chatlists.getChatlistUpdates", parameters: [("chatlist", String(describing: chatlist))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.chatlists.ChatlistUpdates? in - let reader = BufferReader(buffer) - var result: Api.chatlists.ChatlistUpdates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.chatlists.ChatlistUpdates - } - return result - }) - } -} -public extension Api.functions.chatlists { - static func getExportedInvites(chatlist: Api.InputChatlist) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-838608253) - chatlist.serialize(buffer, true) - return (FunctionDescription(name: "chatlists.getExportedInvites", parameters: [("chatlist", String(describing: chatlist))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.chatlists.ExportedInvites? in - let reader = BufferReader(buffer) - var result: Api.chatlists.ExportedInvites? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.chatlists.ExportedInvites - } - return result - }) - } -} -public extension Api.functions.chatlists { - static func getLeaveChatlistSuggestions(chatlist: Api.InputChatlist) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.Peer]>) { - let buffer = Buffer() - buffer.appendInt32(-37955820) - chatlist.serialize(buffer, true) - return (FunctionDescription(name: "chatlists.getLeaveChatlistSuggestions", parameters: [("chatlist", String(describing: chatlist))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.Peer]? in - let reader = BufferReader(buffer) - var result: [Api.Peer]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) - } - return result - }) - } -} -public extension Api.functions.chatlists { - static func hideChatlistUpdates(chatlist: Api.InputChatlist) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1726252795) - chatlist.serialize(buffer, true) - return (FunctionDescription(name: "chatlists.hideChatlistUpdates", parameters: [("chatlist", String(describing: chatlist))]), 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.chatlists { - static func joinChatlistInvite(slug: String, peers: [Api.InputPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1498291302) - serializeString(slug, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(peers.count)) - for item in peers { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "chatlists.joinChatlistInvite", parameters: [("slug", String(describing: slug)), ("peers", String(describing: peers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.chatlists { - static func joinChatlistUpdates(chatlist: Api.InputChatlist, peers: [Api.InputPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-527828747) - chatlist.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(peers.count)) - for item in peers { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "chatlists.joinChatlistUpdates", parameters: [("chatlist", String(describing: chatlist)), ("peers", String(describing: peers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.chatlists { - static func leaveChatlist(chatlist: Api.InputChatlist, peers: [Api.InputPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1962598714) - chatlist.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(peers.count)) - for item in peers { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "chatlists.leaveChatlist", parameters: [("chatlist", String(describing: chatlist)), ("peers", String(describing: peers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.contacts { - static func acceptContact(id: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-130964977) - id.serialize(buffer, true) - return (FunctionDescription(name: "contacts.acceptContact", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.contacts { - static func addContact(flags: Int32, id: Api.InputUser, firstName: String, lastName: String, phone: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-386636848) - serializeInt32(flags, buffer: buffer, boxed: false) - id.serialize(buffer, true) - serializeString(firstName, buffer: buffer, boxed: false) - serializeString(lastName, buffer: buffer, boxed: false) - serializeString(phone, buffer: buffer, boxed: false) - return (FunctionDescription(name: "contacts.addContact", parameters: [("flags", String(describing: flags)), ("id", String(describing: id)), ("firstName", String(describing: firstName)), ("lastName", String(describing: lastName)), ("phone", String(describing: phone))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.contacts { - static func block(flags: Int32, id: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(774801204) - serializeInt32(flags, buffer: buffer, boxed: false) - id.serialize(buffer, true) - return (FunctionDescription(name: "contacts.block", parameters: [("flags", String(describing: flags)), ("id", String(describing: id))]), 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.contacts { - static func blockFromReplies(flags: Int32, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(698914348) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(msgId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "contacts.blockFromReplies", parameters: [("flags", String(describing: flags)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.contacts { - static func deleteByPhones(phones: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(269745566) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(phones.count)) - for item in phones { - serializeString(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "contacts.deleteByPhones", parameters: [("phones", String(describing: phones))]), 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.contacts { - static func deleteContacts(id: [Api.InputUser]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(157945344) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "contacts.deleteContacts", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.contacts { - static func editCloseFriends(id: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1167653392) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt64(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "contacts.editCloseFriends", parameters: [("id", String(describing: id))]), 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.contacts { - static func exportContactToken() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-127582169) - - return (FunctionDescription(name: "contacts.exportContactToken", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedContactToken? in - let reader = BufferReader(buffer) - var result: Api.ExportedContactToken? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.ExportedContactToken - } - return result - }) - } -} -public extension Api.functions.contacts { - static func getBlocked(flags: Int32, offset: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1702457472) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(offset, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "contacts.getBlocked", parameters: [("flags", String(describing: flags)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.contacts.Blocked? in - let reader = BufferReader(buffer) - var result: Api.contacts.Blocked? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.contacts.Blocked - } - return result - }) - } -} -public extension Api.functions.contacts { - static func getContactIDs(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int32]>) { - let buffer = Buffer() - buffer.appendInt32(2061264541) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "contacts.getContactIDs", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int32]? in - let reader = BufferReader(buffer) - var result: [Int32]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - return result - }) - } -} -public extension Api.functions.contacts { - static func getContacts(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1574346258) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "contacts.getContacts", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.contacts.Contacts? in - let reader = BufferReader(buffer) - var result: Api.contacts.Contacts? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.contacts.Contacts - } - return result - }) - } -} -public extension Api.functions.contacts { - static func getLocated(flags: Int32, geoPoint: Api.InputGeoPoint, selfExpires: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-750207932) - serializeInt32(flags, buffer: buffer, boxed: false) - geoPoint.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(selfExpires!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "contacts.getLocated", parameters: [("flags", String(describing: flags)), ("geoPoint", String(describing: geoPoint)), ("selfExpires", String(describing: selfExpires))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.contacts { - static func getSaved() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.SavedContact]>) { - let buffer = Buffer() - buffer.appendInt32(-2098076769) - - return (FunctionDescription(name: "contacts.getSaved", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.SavedContact]? in - let reader = BufferReader(buffer) - var result: [Api.SavedContact]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.SavedContact.self) - } - return result - }) - } -} -public extension Api.functions.contacts { - static func getStatuses() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.ContactStatus]>) { - let buffer = Buffer() - buffer.appendInt32(-995929106) - - return (FunctionDescription(name: "contacts.getStatuses", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.ContactStatus]? in - let reader = BufferReader(buffer) - var result: [Api.ContactStatus]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.ContactStatus.self) - } - return result - }) - } -} -public extension Api.functions.contacts { - static func getTopPeers(flags: Int32, offset: Int32, limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1758168906) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(offset, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "contacts.getTopPeers", parameters: [("flags", String(describing: flags)), ("offset", String(describing: offset)), ("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.contacts.TopPeers? in - let reader = BufferReader(buffer) - var result: Api.contacts.TopPeers? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.contacts.TopPeers - } - return result - }) - } -} -public extension Api.functions.contacts { - static func importContactToken(token: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(318789512) - serializeString(token, buffer: buffer, boxed: false) - return (FunctionDescription(name: "contacts.importContactToken", parameters: [("token", String(describing: token))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.User? in - let reader = BufferReader(buffer) - var result: Api.User? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.User - } - return result - }) - } -} -public extension Api.functions.contacts { - static func importContacts(contacts: [Api.InputContact]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(746589157) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(contacts.count)) - for item in contacts { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "contacts.importContacts", parameters: [("contacts", String(describing: contacts))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.contacts.ImportedContacts? in - let reader = BufferReader(buffer) - var result: Api.contacts.ImportedContacts? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.contacts.ImportedContacts - } - return result - }) - } -} -public extension Api.functions.contacts { - static func resetSaved() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-2020263951) - - return (FunctionDescription(name: "contacts.resetSaved", parameters: []), 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.contacts { - static func resetTopPeerRating(category: Api.TopPeerCategory, peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(451113900) - category.serialize(buffer, true) - peer.serialize(buffer, true) - return (FunctionDescription(name: "contacts.resetTopPeerRating", parameters: [("category", String(describing: category)), ("peer", String(describing: peer))]), 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.contacts { - static func resolvePhone(phone: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1963375804) - serializeString(phone, buffer: buffer, boxed: false) - return (FunctionDescription(name: "contacts.resolvePhone", parameters: [("phone", String(describing: phone))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.contacts.ResolvedPeer? in - let reader = BufferReader(buffer) - var result: Api.contacts.ResolvedPeer? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.contacts.ResolvedPeer - } - return result - }) - } -} -public extension Api.functions.contacts { - static func resolveUsername(username: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-113456221) - serializeString(username, buffer: buffer, boxed: false) - return (FunctionDescription(name: "contacts.resolveUsername", parameters: [("username", String(describing: username))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.contacts.ResolvedPeer? in - let reader = BufferReader(buffer) - var result: Api.contacts.ResolvedPeer? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.contacts.ResolvedPeer - } - return result - }) - } -} -public extension Api.functions.contacts { - static func search(q: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(301470424) - serializeString(q, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "contacts.search", parameters: [("q", String(describing: q)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.contacts.Found? in - let reader = BufferReader(buffer) - var result: Api.contacts.Found? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.contacts.Found - } - return result - }) - } -} -public extension Api.functions.contacts { - static func setBlocked(flags: Int32, id: [Api.InputPeer], limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1798939530) - serializeInt32(flags, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - item.serialize(buffer, true) - } - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "contacts.setBlocked", parameters: [("flags", String(describing: flags)), ("id", String(describing: id)), ("limit", String(describing: limit))]), 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.contacts { - static func toggleTopPeers(enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-2062238246) - enabled.serialize(buffer, true) - return (FunctionDescription(name: "contacts.toggleTopPeers", parameters: [("enabled", String(describing: enabled))]), 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.contacts { - static func unblock(flags: Int32, id: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1252994264) - serializeInt32(flags, buffer: buffer, boxed: false) - id.serialize(buffer, true) - return (FunctionDescription(name: "contacts.unblock", parameters: [("flags", String(describing: flags)), ("id", String(describing: id))]), 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.folders { - static func editPeerFolders(folderPeers: [Api.InputFolderPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1749536939) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(folderPeers.count)) - for item in folderPeers { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "folders.editPeerFolders", parameters: [("folderPeers", String(describing: folderPeers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.help { - static func acceptTermsOfService(id: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-294455398) - id.serialize(buffer, true) - return (FunctionDescription(name: "help.acceptTermsOfService", parameters: [("id", String(describing: id))]), 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.help { - static func dismissSuggestion(peer: Api.InputPeer, suggestion: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-183649631) - peer.serialize(buffer, true) - serializeString(suggestion, buffer: buffer, boxed: false) - return (FunctionDescription(name: "help.dismissSuggestion", parameters: [("peer", String(describing: peer)), ("suggestion", String(describing: suggestion))]), 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.help { - static func editUserInfo(userId: Api.InputUser, message: String, entities: [Api.MessageEntity]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1723407216) - userId.serialize(buffer, true) - serializeString(message, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities.count)) - for item in entities { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "help.editUserInfo", parameters: [("userId", String(describing: userId)), ("message", String(describing: message)), ("entities", String(describing: entities))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.UserInfo? in - let reader = BufferReader(buffer) - var result: Api.help.UserInfo? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.help.UserInfo - } - return result - }) - } -} -public extension Api.functions.help { - static func getAppChangelog(prevAppVersion: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1877938321) - serializeString(prevAppVersion, buffer: buffer, boxed: false) - return (FunctionDescription(name: "help.getAppChangelog", parameters: [("prevAppVersion", String(describing: prevAppVersion))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.help { - static func getAppConfig(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1642330196) - serializeInt32(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "help.getAppConfig", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.AppConfig? in - let reader = BufferReader(buffer) - var result: Api.help.AppConfig? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.help.AppConfig - } - return result - }) - } -} -public extension Api.functions.help { - static func getAppUpdate(source: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1378703997) - serializeString(source, buffer: buffer, boxed: false) - return (FunctionDescription(name: "help.getAppUpdate", parameters: [("source", String(describing: source))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.AppUpdate? in - let reader = BufferReader(buffer) - var result: Api.help.AppUpdate? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.help.AppUpdate - } - return result - }) - } -} -public extension Api.functions.help { - static func getCdnConfig() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1375900482) - - return (FunctionDescription(name: "help.getCdnConfig", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.CdnConfig? in - let reader = BufferReader(buffer) - var result: Api.CdnConfig? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.CdnConfig - } - return result - }) - } -} -public extension Api.functions.help { - static func getConfig() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-990308245) - - return (FunctionDescription(name: "help.getConfig", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Config? in - let reader = BufferReader(buffer) - var result: Api.Config? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Config - } - return result - }) - } -} -public extension Api.functions.help { - static func getCountriesList(langCode: String, hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1935116200) - serializeString(langCode, buffer: buffer, boxed: false) - serializeInt32(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "help.getCountriesList", parameters: [("langCode", String(describing: langCode)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.CountriesList? in - let reader = BufferReader(buffer) - var result: Api.help.CountriesList? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.help.CountriesList - } - return result - }) - } -} -public extension Api.functions.help { - static func getDeepLinkInfo(path: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1072547679) - serializeString(path, buffer: buffer, boxed: false) - return (FunctionDescription(name: "help.getDeepLinkInfo", parameters: [("path", String(describing: path))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.DeepLinkInfo? in - let reader = BufferReader(buffer) - var result: Api.help.DeepLinkInfo? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.help.DeepLinkInfo - } - return result - }) - } -} -public extension Api.functions.help { - static func getInviteText() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1295590211) - - return (FunctionDescription(name: "help.getInviteText", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.InviteText? in - let reader = BufferReader(buffer) - var result: Api.help.InviteText? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.help.InviteText - } - return result - }) - } -} -public extension Api.functions.help { - static func getNearestDc() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(531836966) - - return (FunctionDescription(name: "help.getNearestDc", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.NearestDc? in - let reader = BufferReader(buffer) - var result: Api.NearestDc? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.NearestDc - } - return result - }) - } -} -public extension Api.functions.help { - static func getPassportConfig(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-966677240) - serializeInt32(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "help.getPassportConfig", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.PassportConfig? in - let reader = BufferReader(buffer) - var result: Api.help.PassportConfig? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.help.PassportConfig - } - return result - }) - } -} -public extension Api.functions.help { - static func getPremiumPromo() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1206152236) - - return (FunctionDescription(name: "help.getPremiumPromo", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.PremiumPromo? in - let reader = BufferReader(buffer) - var result: Api.help.PremiumPromo? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.help.PremiumPromo - } - return result - }) - } -} -public extension Api.functions.help { - static func getPromoData() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1063816159) - - return (FunctionDescription(name: "help.getPromoData", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.PromoData? in - let reader = BufferReader(buffer) - var result: Api.help.PromoData? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.help.PromoData - } - return result - }) - } -} -public extension Api.functions.help { - static func getRecentMeUrls(referer: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1036054804) - serializeString(referer, buffer: buffer, boxed: false) - return (FunctionDescription(name: "help.getRecentMeUrls", parameters: [("referer", String(describing: referer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.RecentMeUrls? in - let reader = BufferReader(buffer) - var result: Api.help.RecentMeUrls? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.help.RecentMeUrls - } - return result - }) - } -} -public extension Api.functions.help { - static func getSupport() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1663104819) - - return (FunctionDescription(name: "help.getSupport", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.Support? in - let reader = BufferReader(buffer) - var result: Api.help.Support? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.help.Support - } - return result - }) - } -} -public extension Api.functions.help { - static func getSupportName() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-748624084) - - return (FunctionDescription(name: "help.getSupportName", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.SupportName? in - let reader = BufferReader(buffer) - var result: Api.help.SupportName? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.help.SupportName - } - return result - }) - } -} -public extension Api.functions.help { - static func getTermsOfServiceUpdate() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(749019089) - - return (FunctionDescription(name: "help.getTermsOfServiceUpdate", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.TermsOfServiceUpdate? in - let reader = BufferReader(buffer) - var result: Api.help.TermsOfServiceUpdate? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.help.TermsOfServiceUpdate - } - return result - }) - } -} -public extension Api.functions.help { - static func getUserInfo(userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(59377875) - userId.serialize(buffer, true) - return (FunctionDescription(name: "help.getUserInfo", parameters: [("userId", String(describing: userId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.UserInfo? in - let reader = BufferReader(buffer) - var result: Api.help.UserInfo? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.help.UserInfo - } - return result - }) - } -} -public extension Api.functions.help { - static func hidePromoData(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(505748629) - peer.serialize(buffer, true) - return (FunctionDescription(name: "help.hidePromoData", parameters: [("peer", String(describing: peer))]), 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.help { - static func saveAppLog(events: [Api.InputAppEvent]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1862465352) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(events.count)) - for item in events { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "help.saveAppLog", parameters: [("events", String(describing: events))]), 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.help { - static func setBotUpdatesStatus(pendingUpdatesCount: Int32, message: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-333262899) - serializeInt32(pendingUpdatesCount, buffer: buffer, boxed: false) - serializeString(message, buffer: buffer, boxed: false) - return (FunctionDescription(name: "help.setBotUpdatesStatus", parameters: [("pendingUpdatesCount", String(describing: pendingUpdatesCount)), ("message", String(describing: message))]), 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.help { - static func test() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1058929929) - - return (FunctionDescription(name: "help.test", parameters: []), 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.langpack { - static func getDifference(langPack: String, langCode: String, fromVersion: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-845657435) - serializeString(langPack, buffer: buffer, boxed: false) - serializeString(langCode, buffer: buffer, boxed: false) - serializeInt32(fromVersion, buffer: buffer, boxed: false) - return (FunctionDescription(name: "langpack.getDifference", parameters: [("langPack", String(describing: langPack)), ("langCode", String(describing: langCode)), ("fromVersion", String(describing: fromVersion))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.LangPackDifference? in - let reader = BufferReader(buffer) - var result: Api.LangPackDifference? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.LangPackDifference - } - return result - }) - } -} -public extension Api.functions.langpack { - static func getLangPack(langPack: String, langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-219008246) - serializeString(langPack, buffer: buffer, boxed: false) - serializeString(langCode, buffer: buffer, boxed: false) - return (FunctionDescription(name: "langpack.getLangPack", parameters: [("langPack", String(describing: langPack)), ("langCode", String(describing: langCode))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.LangPackDifference? in - let reader = BufferReader(buffer) - var result: Api.LangPackDifference? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.LangPackDifference - } - return result - }) - } -} -public extension Api.functions.langpack { - static func getLanguage(langPack: String, langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1784243458) - serializeString(langPack, buffer: buffer, boxed: false) - serializeString(langCode, buffer: buffer, boxed: false) - return (FunctionDescription(name: "langpack.getLanguage", parameters: [("langPack", String(describing: langPack)), ("langCode", String(describing: langCode))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.LangPackLanguage? in - let reader = BufferReader(buffer) - var result: Api.LangPackLanguage? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.LangPackLanguage - } - return result - }) - } -} -public extension Api.functions.langpack { - static func getLanguages(langPack: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.LangPackLanguage]>) { - let buffer = Buffer() - buffer.appendInt32(1120311183) - serializeString(langPack, buffer: buffer, boxed: false) - return (FunctionDescription(name: "langpack.getLanguages", parameters: [("langPack", String(describing: langPack))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.LangPackLanguage]? in - let reader = BufferReader(buffer) - var result: [Api.LangPackLanguage]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.LangPackLanguage.self) - } - return result - }) - } -} -public extension Api.functions.langpack { - static func getStrings(langPack: String, langCode: String, keys: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.LangPackString]>) { - let buffer = Buffer() - buffer.appendInt32(-269862909) - serializeString(langPack, buffer: buffer, boxed: false) - serializeString(langCode, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(keys.count)) - for item in keys { - serializeString(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "langpack.getStrings", parameters: [("langPack", String(describing: langPack)), ("langCode", String(describing: langCode)), ("keys", String(describing: keys))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.LangPackString]? in - let reader = BufferReader(buffer) - var result: [Api.LangPackString]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.LangPackString.self) - } - return result - }) - } -} -public extension Api.functions.messages { - static func acceptEncryption(peer: Api.InputEncryptedChat, gB: Buffer, keyFingerprint: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1035731989) - peer.serialize(buffer, true) - serializeBytes(gB, buffer: buffer, boxed: false) - serializeInt64(keyFingerprint, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.acceptEncryption", parameters: [("peer", String(describing: peer)), ("gB", String(describing: gB)), ("keyFingerprint", String(describing: keyFingerprint))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EncryptedChat? in - let reader = BufferReader(buffer) - var result: Api.EncryptedChat? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.EncryptedChat - } - return result - }) - } -} -public extension Api.functions.messages { - static func acceptUrlAuth(flags: Int32, peer: Api.InputPeer?, msgId: Int32?, buttonId: Int32?, url: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1322487515) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {peer!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(msgId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(buttonId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(url!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "messages.acceptUrlAuth", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("buttonId", String(describing: buttonId)), ("url", String(describing: url))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.UrlAuthResult? in - let reader = BufferReader(buffer) - var result: Api.UrlAuthResult? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.UrlAuthResult - } - return result - }) - } -} -public extension Api.functions.messages { - static func addChatUser(chatId: Int64, userId: Api.InputUser, fwdLimit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-230206493) - serializeInt64(chatId, buffer: buffer, boxed: false) - userId.serialize(buffer, true) - serializeInt32(fwdLimit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.addChatUser", parameters: [("chatId", String(describing: chatId)), ("userId", String(describing: userId)), ("fwdLimit", String(describing: fwdLimit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func checkChatInvite(hash: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1051570619) - serializeString(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.checkChatInvite", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ChatInvite? in - let reader = BufferReader(buffer) - var result: Api.ChatInvite? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.ChatInvite - } - return result - }) - } -} -public extension Api.functions.messages { - static func checkHistoryImport(importHead: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1140726259) - serializeString(importHead, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.checkHistoryImport", parameters: [("importHead", String(describing: importHead))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.HistoryImportParsed? in - let reader = BufferReader(buffer) - var result: Api.messages.HistoryImportParsed? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.HistoryImportParsed - } - return result - }) - } -} -public extension Api.functions.messages { - static func checkHistoryImportPeer(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1573261059) - peer.serialize(buffer, true) - return (FunctionDescription(name: "messages.checkHistoryImportPeer", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.CheckedHistoryImportPeer? in - let reader = BufferReader(buffer) - var result: Api.messages.CheckedHistoryImportPeer? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.CheckedHistoryImportPeer - } - return result - }) - } -} -public extension Api.functions.messages { - static func clearAllDrafts() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2119757468) - - return (FunctionDescription(name: "messages.clearAllDrafts", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func clearRecentReactions() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1644236876) - - return (FunctionDescription(name: "messages.clearRecentReactions", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func clearRecentStickers(flags: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1986437075) - serializeInt32(flags, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.clearRecentStickers", parameters: [("flags", String(describing: flags))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func createChat(flags: Int32, users: [Api.InputUser], title: String, ttlPeriod: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(3450904) - serializeInt32(flags, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - serializeString(title, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "messages.createChat", parameters: [("flags", String(describing: flags)), ("users", String(describing: users)), ("title", String(describing: title)), ("ttlPeriod", String(describing: ttlPeriod))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func deleteChat(chatId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1540419152) - serializeInt64(chatId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.deleteChat", parameters: [("chatId", String(describing: chatId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func deleteChatUser(flags: Int32, chatId: Int64, userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1575461717) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(chatId, buffer: buffer, boxed: false) - userId.serialize(buffer, true) - return (FunctionDescription(name: "messages.deleteChatUser", parameters: [("flags", String(describing: flags)), ("chatId", String(describing: chatId)), ("userId", String(describing: userId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func deleteExportedChatInvite(peer: Api.InputPeer, link: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-731601877) - peer.serialize(buffer, true) - serializeString(link, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.deleteExportedChatInvite", parameters: [("peer", String(describing: peer)), ("link", String(describing: link))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func deleteHistory(flags: Int32, peer: Api.InputPeer, maxId: Int32, minDate: Int32?, maxDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1332768214) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(maxId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {serializeInt32(minDate!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {serializeInt32(maxDate!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "messages.deleteHistory", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("maxId", String(describing: maxId)), ("minDate", String(describing: minDate)), ("maxDate", String(describing: maxDate))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedHistory? in - let reader = BufferReader(buffer) - var result: Api.messages.AffectedHistory? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.AffectedHistory - } - return result - }) - } -} -public extension Api.functions.messages { - static func deleteMessages(flags: Int32, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-443640366) - serializeInt32(flags, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "messages.deleteMessages", parameters: [("flags", String(describing: flags)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedMessages? in - let reader = BufferReader(buffer) - var result: Api.messages.AffectedMessages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.AffectedMessages - } - return result - }) - } -} -public extension Api.functions.messages { - static func deletePhoneCallHistory(flags: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-104078327) - serializeInt32(flags, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.deletePhoneCallHistory", parameters: [("flags", String(describing: flags))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedFoundMessages? in - let reader = BufferReader(buffer) - var result: Api.messages.AffectedFoundMessages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.AffectedFoundMessages - } - return result - }) - } -} -public extension Api.functions.messages { - static func deleteRevokedExportedChatInvites(peer: Api.InputPeer, adminId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1452833749) - peer.serialize(buffer, true) - adminId.serialize(buffer, true) - return (FunctionDescription(name: "messages.deleteRevokedExportedChatInvites", parameters: [("peer", String(describing: peer)), ("adminId", String(describing: adminId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func deleteScheduledMessages(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1504586518) - peer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "messages.deleteScheduledMessages", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func discardEncryption(flags: Int32, chatId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-208425312) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(chatId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.discardEncryption", parameters: [("flags", String(describing: flags)), ("chatId", String(describing: chatId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func editChatAbout(peer: Api.InputPeer, about: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-554301545) - peer.serialize(buffer, true) - serializeString(about, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.editChatAbout", parameters: [("peer", String(describing: peer)), ("about", String(describing: about))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func editChatAdmin(chatId: Int64, userId: Api.InputUser, isAdmin: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1470377534) - serializeInt64(chatId, buffer: buffer, boxed: false) - userId.serialize(buffer, true) - isAdmin.serialize(buffer, true) - return (FunctionDescription(name: "messages.editChatAdmin", parameters: [("chatId", String(describing: chatId)), ("userId", String(describing: userId)), ("isAdmin", String(describing: isAdmin))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func editChatDefaultBannedRights(peer: Api.InputPeer, bannedRights: Api.ChatBannedRights) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1517917375) - peer.serialize(buffer, true) - bannedRights.serialize(buffer, true) - return (FunctionDescription(name: "messages.editChatDefaultBannedRights", parameters: [("peer", String(describing: peer)), ("bannedRights", String(describing: bannedRights))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func editChatPhoto(chatId: Int64, photo: Api.InputChatPhoto) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(903730804) - serializeInt64(chatId, buffer: buffer, boxed: false) - photo.serialize(buffer, true) - return (FunctionDescription(name: "messages.editChatPhoto", parameters: [("chatId", String(describing: chatId)), ("photo", String(describing: photo))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func editChatTitle(chatId: Int64, title: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1937260541) - serializeInt64(chatId, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.editChatTitle", parameters: [("chatId", String(describing: chatId)), ("title", String(describing: title))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func editExportedChatInvite(flags: Int32, peer: Api.InputPeer, link: String, expireDate: Int32?, usageLimit: Int32?, requestNeeded: Api.Bool?, title: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1110823051) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeString(link, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(expireDate!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(usageLimit!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {requestNeeded!.serialize(buffer, true)} - if Int(flags) & Int(1 << 4) != 0 {serializeString(title!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "messages.editExportedChatInvite", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("link", String(describing: link)), ("expireDate", String(describing: expireDate)), ("usageLimit", String(describing: usageLimit)), ("requestNeeded", String(describing: requestNeeded)), ("title", String(describing: title))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ExportedChatInvite? in - let reader = BufferReader(buffer) - var result: Api.messages.ExportedChatInvite? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.ExportedChatInvite - } - return result - }) - } -} -public extension Api.functions.messages { - static func editInlineBotMessage(flags: Int32, id: Api.InputBotInlineMessageID, message: String?, media: Api.InputMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-2091549254) - serializeInt32(flags, buffer: buffer, boxed: false) - id.serialize(buffer, true) - if Int(flags) & Int(1 << 11) != 0 {serializeString(message!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 14) != 0 {media!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} - if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities!.count)) - for item in entities! { - item.serialize(buffer, true) - }} - return (FunctionDescription(name: "messages.editInlineBotMessage", parameters: [("flags", String(describing: flags)), ("id", String(describing: id)), ("message", String(describing: message)), ("media", String(describing: media)), ("replyMarkup", String(describing: replyMarkup)), ("entities", String(describing: entities))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func editMessage(flags: Int32, peer: Api.InputPeer, id: Int32, message: String?, media: Api.InputMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1224152952) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 11) != 0 {serializeString(message!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 14) != 0 {media!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} - if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities!.count)) - for item in entities! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 15) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "messages.editMessage", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id)), ("message", String(describing: message)), ("media", String(describing: media)), ("replyMarkup", String(describing: replyMarkup)), ("entities", String(describing: entities)), ("scheduleDate", String(describing: scheduleDate))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func exportChatInvite(flags: Int32, peer: Api.InputPeer, expireDate: Int32?, usageLimit: Int32?, title: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1607670315) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(expireDate!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(usageLimit!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {serializeString(title!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "messages.exportChatInvite", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("expireDate", String(describing: expireDate)), ("usageLimit", String(describing: usageLimit)), ("title", String(describing: title))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedChatInvite? in - let reader = BufferReader(buffer) - var result: Api.ExportedChatInvite? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite - } - return result - }) - } -} -public extension Api.functions.messages { - static func faveSticker(id: Api.InputDocument, unfave: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1174420133) - id.serialize(buffer, true) - unfave.serialize(buffer, true) - return (FunctionDescription(name: "messages.faveSticker", parameters: [("id", String(describing: id)), ("unfave", String(describing: unfave))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func forwardMessages(flags: Int32, fromPeer: Api.InputPeer, id: [Int32], randomId: [Int64], toPeer: Api.InputPeer, topMsgId: Int32?, scheduleDate: Int32?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-966673468) - serializeInt32(flags, buffer: buffer, boxed: false) - fromPeer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(randomId.count)) - for item in randomId { - serializeInt64(item, buffer: buffer, boxed: false) - } - toPeer.serialize(buffer, true) - if Int(flags) & Int(1 << 9) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)} - return (FunctionDescription(name: "messages.forwardMessages", parameters: [("flags", String(describing: flags)), ("fromPeer", String(describing: fromPeer)), ("id", String(describing: id)), ("randomId", String(describing: randomId)), ("toPeer", String(describing: toPeer)), ("topMsgId", String(describing: topMsgId)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func getAdminsWithInvites(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(958457583) - peer.serialize(buffer, true) - return (FunctionDescription(name: "messages.getAdminsWithInvites", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ChatAdminsWithInvites? in - let reader = BufferReader(buffer) - var result: Api.messages.ChatAdminsWithInvites? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.ChatAdminsWithInvites - } - return result - }) - } -} -public extension Api.functions.messages { - static func getAllDrafts() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1782549861) - - return (FunctionDescription(name: "messages.getAllDrafts", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func getAllStickers(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1197432408) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getAllStickers", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AllStickers? in - let reader = BufferReader(buffer) - var result: Api.messages.AllStickers? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.AllStickers - } - return result - }) - } -} -public extension Api.functions.messages { - static func getArchivedStickers(flags: Int32, offsetId: Int64, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1475442322) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(offsetId, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getArchivedStickers", parameters: [("flags", String(describing: flags)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ArchivedStickers? in - let reader = BufferReader(buffer) - var result: Api.messages.ArchivedStickers? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.ArchivedStickers - } - return result - }) - } -} -public extension Api.functions.messages { - static func getAttachMenuBot(bot: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1998676370) - bot.serialize(buffer, true) - return (FunctionDescription(name: "messages.getAttachMenuBot", parameters: [("bot", String(describing: bot))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.AttachMenuBotsBot? in - let reader = BufferReader(buffer) - var result: Api.AttachMenuBotsBot? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.AttachMenuBotsBot - } - return result - }) - } -} -public extension Api.functions.messages { - static func getAttachMenuBots(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(385663691) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getAttachMenuBots", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.AttachMenuBots? in - let reader = BufferReader(buffer) - var result: Api.AttachMenuBots? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.AttachMenuBots - } - return result - }) - } -} -public extension Api.functions.messages { - static func getAttachedStickers(media: Api.InputStickeredMedia) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.StickerSetCovered]>) { - let buffer = Buffer() - buffer.appendInt32(-866424884) - media.serialize(buffer, true) - return (FunctionDescription(name: "messages.getAttachedStickers", parameters: [("media", String(describing: media))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.StickerSetCovered]? in - let reader = BufferReader(buffer) - var result: [Api.StickerSetCovered]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self) - } - return result - }) - } -} -public extension Api.functions.messages { - static func getAvailableReactions(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(417243308) - serializeInt32(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getAvailableReactions", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AvailableReactions? in - let reader = BufferReader(buffer) - var result: Api.messages.AvailableReactions? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.AvailableReactions - } - return result - }) - } -} -public extension Api.functions.messages { - static func getBotApp(app: Api.InputBotApp, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(889046467) - app.serialize(buffer, true) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getBotApp", parameters: [("app", String(describing: app)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.BotApp? in - let reader = BufferReader(buffer) - var result: Api.messages.BotApp? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.BotApp - } - return result - }) - } -} -public extension Api.functions.messages { - static func getBotCallbackAnswer(flags: Int32, peer: Api.InputPeer, msgId: Int32, data: Buffer?, password: Api.InputCheckPasswordSRP?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1824339449) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeBytes(data!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {password!.serialize(buffer, true)} - return (FunctionDescription(name: "messages.getBotCallbackAnswer", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("data", String(describing: data)), ("password", String(describing: password))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.BotCallbackAnswer? in - let reader = BufferReader(buffer) - var result: Api.messages.BotCallbackAnswer? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.BotCallbackAnswer - } - return result - }) - } -} -public extension Api.functions.messages { - static func getChatInviteImporters(flags: Int32, peer: Api.InputPeer, link: String?, q: String?, offsetDate: Int32, offsetUser: Api.InputUser, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-553329330) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 1) != 0 {serializeString(link!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(q!, buffer: buffer, boxed: false)} - serializeInt32(offsetDate, buffer: buffer, boxed: false) - offsetUser.serialize(buffer, true) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getChatInviteImporters", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("link", String(describing: link)), ("q", String(describing: q)), ("offsetDate", String(describing: offsetDate)), ("offsetUser", String(describing: offsetUser)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ChatInviteImporters? in - let reader = BufferReader(buffer) - var result: Api.messages.ChatInviteImporters? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.ChatInviteImporters - } - return result - }) - } -} -public extension Api.functions.messages { - static func getChats(id: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1240027791) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt64(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "messages.getChats", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Chats? in - let reader = BufferReader(buffer) - var result: Api.messages.Chats? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Chats - } - return result - }) - } -} -public extension Api.functions.messages { - static func getCommonChats(userId: Api.InputUser, maxId: Int64, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-468934396) - userId.serialize(buffer, true) - serializeInt64(maxId, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getCommonChats", parameters: [("userId", String(describing: userId)), ("maxId", String(describing: maxId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Chats? in - let reader = BufferReader(buffer) - var result: Api.messages.Chats? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Chats - } - return result - }) - } -} -public extension Api.functions.messages { - static func getCustomEmojiDocuments(documentId: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.Document]>) { - let buffer = Buffer() - buffer.appendInt32(-643100844) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(documentId.count)) - for item in documentId { - serializeInt64(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "messages.getCustomEmojiDocuments", parameters: [("documentId", String(describing: documentId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.Document]? in - let reader = BufferReader(buffer) - var result: [Api.Document]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) - } - return result - }) - } -} -public extension Api.functions.messages { - static func getDefaultHistoryTTL() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1703637384) - - return (FunctionDescription(name: "messages.getDefaultHistoryTTL", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.DefaultHistoryTTL? in - let reader = BufferReader(buffer) - var result: Api.DefaultHistoryTTL? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.DefaultHistoryTTL - } - return result - }) - } -} -public extension Api.functions.messages { - static func getDhConfig(version: Int32, randomLength: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(651135312) - serializeInt32(version, buffer: buffer, boxed: false) - serializeInt32(randomLength, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getDhConfig", parameters: [("version", String(describing: version)), ("randomLength", String(describing: randomLength))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.DhConfig? in - let reader = BufferReader(buffer) - var result: Api.messages.DhConfig? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.DhConfig - } - return result - }) - } -} -public extension Api.functions.messages { - static func getDialogFilters() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.DialogFilter]>) { - let buffer = Buffer() - buffer.appendInt32(-241247891) - - return (FunctionDescription(name: "messages.getDialogFilters", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.DialogFilter]? in - let reader = BufferReader(buffer) - var result: [Api.DialogFilter]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.DialogFilter.self) - } - return result - }) - } -} -public extension Api.functions.messages { - static func getDialogUnreadMarks() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.DialogPeer]>) { - let buffer = Buffer() - buffer.appendInt32(585256482) - - return (FunctionDescription(name: "messages.getDialogUnreadMarks", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.DialogPeer]? in - let reader = BufferReader(buffer) - var result: [Api.DialogPeer]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.DialogPeer.self) - } - return result - }) - } -} -public extension Api.functions.messages { - static func getDialogs(flags: Int32, folderId: Int32?, offsetDate: Int32, offsetId: Int32, offsetPeer: Api.InputPeer, limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1594569905) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} - serializeInt32(offsetDate, buffer: buffer, boxed: false) - serializeInt32(offsetId, buffer: buffer, boxed: false) - offsetPeer.serialize(buffer, true) - serializeInt32(limit, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getDialogs", parameters: [("flags", String(describing: flags)), ("folderId", String(describing: folderId)), ("offsetDate", String(describing: offsetDate)), ("offsetId", String(describing: offsetId)), ("offsetPeer", String(describing: offsetPeer)), ("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Dialogs? in - let reader = BufferReader(buffer) - var result: Api.messages.Dialogs? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Dialogs - } - return result - }) - } -} -public extension Api.functions.messages { - static func getDiscussionMessage(peer: Api.InputPeer, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1147761405) - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getDiscussionMessage", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.DiscussionMessage? in - let reader = BufferReader(buffer) - var result: Api.messages.DiscussionMessage? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.DiscussionMessage - } - return result - }) - } -} -public extension Api.functions.messages { - static func getDocumentByHash(sha256: Buffer, size: Int64, mimeType: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1309538785) - serializeBytes(sha256, buffer: buffer, boxed: false) - serializeInt64(size, buffer: buffer, boxed: false) - serializeString(mimeType, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getDocumentByHash", parameters: [("sha256", String(describing: sha256)), ("size", String(describing: size)), ("mimeType", String(describing: mimeType))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Document? in - let reader = BufferReader(buffer) - var result: Api.Document? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Document - } - return result - }) - } -} -public extension Api.functions.messages { - static func getEmojiGroups(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1955122779) - serializeInt32(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getEmojiGroups", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.EmojiGroups? in - let reader = BufferReader(buffer) - var result: Api.messages.EmojiGroups? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.EmojiGroups - } - return result - }) - } -} -public extension Api.functions.messages { - static func getEmojiKeywords(langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(899735650) - serializeString(langCode, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getEmojiKeywords", parameters: [("langCode", String(describing: langCode))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EmojiKeywordsDifference? in - let reader = BufferReader(buffer) - var result: Api.EmojiKeywordsDifference? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.EmojiKeywordsDifference - } - return result - }) - } -} -public extension Api.functions.messages { - static func getEmojiKeywordsDifference(langCode: String, fromVersion: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(352892591) - serializeString(langCode, buffer: buffer, boxed: false) - serializeInt32(fromVersion, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getEmojiKeywordsDifference", parameters: [("langCode", String(describing: langCode)), ("fromVersion", String(describing: fromVersion))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EmojiKeywordsDifference? in - let reader = BufferReader(buffer) - var result: Api.EmojiKeywordsDifference? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.EmojiKeywordsDifference - } - return result - }) - } -} -public extension Api.functions.messages { - static func getEmojiKeywordsLanguages(langCodes: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.EmojiLanguage]>) { - let buffer = Buffer() - buffer.appendInt32(1318675378) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(langCodes.count)) - for item in langCodes { - serializeString(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "messages.getEmojiKeywordsLanguages", parameters: [("langCodes", String(describing: langCodes))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.EmojiLanguage]? in - let reader = BufferReader(buffer) - var result: [Api.EmojiLanguage]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.EmojiLanguage.self) - } - return result - }) - } -} -public extension Api.functions.messages { - static func getEmojiProfilePhotoGroups(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(564480243) - serializeInt32(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getEmojiProfilePhotoGroups", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.EmojiGroups? in - let reader = BufferReader(buffer) - var result: Api.messages.EmojiGroups? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.EmojiGroups - } - return result - }) - } -} -public extension Api.functions.messages { - static func getEmojiStatusGroups(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(785209037) - serializeInt32(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getEmojiStatusGroups", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.EmojiGroups? in - let reader = BufferReader(buffer) - var result: Api.messages.EmojiGroups? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.EmojiGroups - } - return result - }) - } -} -public extension Api.functions.messages { - static func getEmojiStickers(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-67329649) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getEmojiStickers", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AllStickers? in - let reader = BufferReader(buffer) - var result: Api.messages.AllStickers? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.AllStickers - } - return result - }) - } -} -public extension Api.functions.messages { - static func getEmojiURL(langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-709817306) - serializeString(langCode, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getEmojiURL", parameters: [("langCode", String(describing: langCode))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EmojiURL? in - let reader = BufferReader(buffer) - var result: Api.EmojiURL? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.EmojiURL - } - return result - }) - } -} -public extension Api.functions.messages { - static func getExportedChatInvite(peer: Api.InputPeer, link: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1937010524) - peer.serialize(buffer, true) - serializeString(link, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getExportedChatInvite", parameters: [("peer", String(describing: peer)), ("link", String(describing: link))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ExportedChatInvite? in - let reader = BufferReader(buffer) - var result: Api.messages.ExportedChatInvite? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.ExportedChatInvite - } - return result - }) - } -} -public extension Api.functions.messages { - static func getExportedChatInvites(flags: Int32, peer: Api.InputPeer, adminId: Api.InputUser, offsetDate: Int32?, offsetLink: String?, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1565154314) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - adminId.serialize(buffer, true) - if Int(flags) & Int(1 << 2) != 0 {serializeInt32(offsetDate!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(offsetLink!, buffer: buffer, boxed: false)} - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getExportedChatInvites", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("adminId", String(describing: adminId)), ("offsetDate", String(describing: offsetDate)), ("offsetLink", String(describing: offsetLink)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ExportedChatInvites? in - let reader = BufferReader(buffer) - var result: Api.messages.ExportedChatInvites? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.ExportedChatInvites - } - return result - }) - } -} -public extension Api.functions.messages { - static func getExtendedMedia(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-2064119788) - peer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "messages.getExtendedMedia", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func getFavedStickers(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(82946729) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getFavedStickers", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.FavedStickers? in - let reader = BufferReader(buffer) - var result: Api.messages.FavedStickers? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.FavedStickers - } - return result - }) - } -} -public extension Api.functions.messages { - static func getFeaturedEmojiStickers(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(248473398) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getFeaturedEmojiStickers", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.FeaturedStickers? in - let reader = BufferReader(buffer) - var result: Api.messages.FeaturedStickers? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.FeaturedStickers - } - return result - }) - } -} -public extension Api.functions.messages { - static func getFeaturedStickers(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1685588756) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getFeaturedStickers", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.FeaturedStickers? in - let reader = BufferReader(buffer) - var result: Api.messages.FeaturedStickers? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.FeaturedStickers - } - return result - }) - } -} -public extension Api.functions.messages { - static func getFullChat(chatId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1364194508) - serializeInt64(chatId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getFullChat", parameters: [("chatId", String(describing: chatId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ChatFull? in - let reader = BufferReader(buffer) - var result: Api.messages.ChatFull? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.ChatFull - } - return result - }) - } -} -public extension Api.functions.messages { - static func getGameHighScores(peer: Api.InputPeer, id: Int32, userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-400399203) - peer.serialize(buffer, true) - serializeInt32(id, buffer: buffer, boxed: false) - userId.serialize(buffer, true) - return (FunctionDescription(name: "messages.getGameHighScores", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("userId", String(describing: userId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.HighScores? in - let reader = BufferReader(buffer) - var result: Api.messages.HighScores? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.HighScores - } - return result - }) - } -} -public extension Api.functions.messages { - static func getHistory(peer: Api.InputPeer, offsetId: Int32, offsetDate: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1143203525) - peer.serialize(buffer, true) - serializeInt32(offsetId, buffer: buffer, boxed: false) - serializeInt32(offsetDate, buffer: buffer, boxed: false) - serializeInt32(addOffset, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - serializeInt32(maxId, buffer: buffer, boxed: false) - serializeInt32(minId, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getHistory", parameters: [("peer", String(describing: peer)), ("offsetId", String(describing: offsetId)), ("offsetDate", String(describing: offsetDate)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in - let reader = BufferReader(buffer) - var result: Api.messages.Messages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Messages - } - return result - }) - } -} -public extension Api.functions.messages { - static func getInlineBotResults(flags: Int32, bot: Api.InputUser, peer: Api.InputPeer, geoPoint: Api.InputGeoPoint?, query: String, offset: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1364105629) - serializeInt32(flags, buffer: buffer, boxed: false) - bot.serialize(buffer, true) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {geoPoint!.serialize(buffer, true)} - serializeString(query, buffer: buffer, boxed: false) - serializeString(offset, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getInlineBotResults", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("peer", String(describing: peer)), ("geoPoint", String(describing: geoPoint)), ("query", String(describing: query)), ("offset", String(describing: offset))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.BotResults? in - let reader = BufferReader(buffer) - var result: Api.messages.BotResults? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.BotResults - } - return result - }) - } -} -public extension Api.functions.messages { - static func getInlineGameHighScores(id: Api.InputBotInlineMessageID, userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(258170395) - id.serialize(buffer, true) - userId.serialize(buffer, true) - return (FunctionDescription(name: "messages.getInlineGameHighScores", parameters: [("id", String(describing: id)), ("userId", String(describing: userId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.HighScores? in - let reader = BufferReader(buffer) - var result: Api.messages.HighScores? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.HighScores - } - return result - }) - } -} -public extension Api.functions.messages { - static func getMaskStickers(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1678738104) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getMaskStickers", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AllStickers? in - let reader = BufferReader(buffer) - var result: Api.messages.AllStickers? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.AllStickers - } - return result - }) - } -} -public extension Api.functions.messages { - static func getMessageEditData(peer: Api.InputPeer, id: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-39416522) - peer.serialize(buffer, true) - serializeInt32(id, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getMessageEditData", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.MessageEditData? in - let reader = BufferReader(buffer) - var result: Api.messages.MessageEditData? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.MessageEditData - } - return result - }) - } -} -public extension Api.functions.messages { - static func getMessageReactionsList(flags: Int32, peer: Api.InputPeer, id: Int32, reaction: Api.Reaction?, offset: String?, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1176190792) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {reaction!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {serializeString(offset!, buffer: buffer, boxed: false)} - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getMessageReactionsList", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id)), ("reaction", String(describing: reaction)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.MessageReactionsList? in - let reader = BufferReader(buffer) - var result: Api.messages.MessageReactionsList? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.MessageReactionsList - } - return result - }) - } -} -public extension Api.functions.messages { - static func getMessageReadParticipants(peer: Api.InputPeer, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.ReadParticipantDate]>) { - let buffer = Buffer() - buffer.appendInt32(834782287) - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getMessageReadParticipants", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.ReadParticipantDate]? in - let reader = BufferReader(buffer) - var result: [Api.ReadParticipantDate]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.ReadParticipantDate.self) - } - return result - }) - } -} -public extension Api.functions.messages { - static func getMessages(id: [Api.InputMessage]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1673946374) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "messages.getMessages", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in - let reader = BufferReader(buffer) - var result: Api.messages.Messages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Messages - } - return result - }) - } -} -public extension Api.functions.messages { - static func getMessagesReactions(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1950707482) - peer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "messages.getMessagesReactions", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func getMessagesViews(peer: Api.InputPeer, id: [Int32], increment: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1468322785) - peer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) - } - increment.serialize(buffer, true) - return (FunctionDescription(name: "messages.getMessagesViews", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("increment", String(describing: increment))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.MessageViews? in - let reader = BufferReader(buffer) - var result: Api.messages.MessageViews? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.MessageViews - } - return result - }) - } -} -public extension Api.functions.messages { - static func getOldFeaturedStickers(offset: Int32, limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2127598753) - serializeInt32(offset, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getOldFeaturedStickers", parameters: [("offset", String(describing: offset)), ("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.FeaturedStickers? in - let reader = BufferReader(buffer) - var result: Api.messages.FeaturedStickers? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.FeaturedStickers - } - return result - }) - } -} -public extension Api.functions.messages { - static func getOnlines(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1848369232) - peer.serialize(buffer, true) - return (FunctionDescription(name: "messages.getOnlines", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ChatOnlines? in - let reader = BufferReader(buffer) - var result: Api.ChatOnlines? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.ChatOnlines - } - return result - }) - } -} -public extension Api.functions.messages { - static func getPeerDialogs(peers: [Api.InputDialogPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-462373635) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(peers.count)) - for item in peers { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "messages.getPeerDialogs", parameters: [("peers", String(describing: peers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.PeerDialogs? in - let reader = BufferReader(buffer) - var result: Api.messages.PeerDialogs? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.PeerDialogs - } - return result - }) - } -} -public extension Api.functions.messages { - static func getPeerSettings(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-270948702) - peer.serialize(buffer, true) - return (FunctionDescription(name: "messages.getPeerSettings", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.PeerSettings? in - let reader = BufferReader(buffer) - var result: Api.messages.PeerSettings? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.PeerSettings - } - return result - }) - } -} -public extension Api.functions.messages { - static func getPinnedDialogs(folderId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-692498958) - serializeInt32(folderId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getPinnedDialogs", parameters: [("folderId", String(describing: folderId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.PeerDialogs? in - let reader = BufferReader(buffer) - var result: Api.messages.PeerDialogs? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.PeerDialogs - } - return result - }) - } -} -public extension Api.functions.messages { - static func getPollResults(peer: Api.InputPeer, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1941660731) - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getPollResults", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func getPollVotes(flags: Int32, peer: Api.InputPeer, id: Int32, option: Buffer?, offset: String?, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1200736242) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeBytes(option!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeString(offset!, buffer: buffer, boxed: false)} - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getPollVotes", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id)), ("option", String(describing: option)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.VotesList? in - let reader = BufferReader(buffer) - var result: Api.messages.VotesList? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.VotesList - } - return result - }) - } -} -public extension Api.functions.messages { - static func getRecentLocations(peer: Api.InputPeer, limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1881817312) - peer.serialize(buffer, true) - serializeInt32(limit, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getRecentLocations", parameters: [("peer", String(describing: peer)), ("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in - let reader = BufferReader(buffer) - var result: Api.messages.Messages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Messages - } - return result - }) - } -} -public extension Api.functions.messages { - static func getRecentReactions(limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(960896434) - serializeInt32(limit, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getRecentReactions", parameters: [("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Reactions? in - let reader = BufferReader(buffer) - var result: Api.messages.Reactions? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Reactions - } - return result - }) - } -} -public extension Api.functions.messages { - static func getRecentStickers(flags: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1649852357) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getRecentStickers", parameters: [("flags", String(describing: flags)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.RecentStickers? in - let reader = BufferReader(buffer) - var result: Api.messages.RecentStickers? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.RecentStickers - } - return result - }) - } -} -public extension Api.functions.messages { - static func getReplies(peer: Api.InputPeer, msgId: Int32, offsetId: Int32, offsetDate: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(584962828) - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - serializeInt32(offsetId, buffer: buffer, boxed: false) - serializeInt32(offsetDate, buffer: buffer, boxed: false) - serializeInt32(addOffset, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - serializeInt32(maxId, buffer: buffer, boxed: false) - serializeInt32(minId, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getReplies", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("offsetId", String(describing: offsetId)), ("offsetDate", String(describing: offsetDate)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in - let reader = BufferReader(buffer) - var result: Api.messages.Messages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Messages - } - return result - }) - } -} -public extension Api.functions.messages { - static func getSavedGifs(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1559270965) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getSavedGifs", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SavedGifs? in - let reader = BufferReader(buffer) - var result: Api.messages.SavedGifs? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.SavedGifs - } - return result - }) - } -} -public extension Api.functions.messages { - static func getScheduledHistory(peer: Api.InputPeer, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-183077365) - peer.serialize(buffer, true) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getScheduledHistory", parameters: [("peer", String(describing: peer)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in - let reader = BufferReader(buffer) - var result: Api.messages.Messages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Messages - } - return result - }) - } -} -public extension Api.functions.messages { - static func getScheduledMessages(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1111817116) - peer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "messages.getScheduledMessages", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in - let reader = BufferReader(buffer) - var result: Api.messages.Messages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Messages - } - return result - }) - } -} -public extension Api.functions.messages { - static func getSearchCounters(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?, filters: [Api.MessagesFilter]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.messages.SearchCounter]>) { - let buffer = Buffer() - buffer.appendInt32(11435201) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(filters.count)) - for item in filters { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "messages.getSearchCounters", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId)), ("filters", String(describing: filters))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.messages.SearchCounter]? in - let reader = BufferReader(buffer) - var result: [Api.messages.SearchCounter]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.messages.SearchCounter.self) - } - return result - }) - } -} -public extension Api.functions.messages { - static func getSearchResultsCalendar(peer: Api.InputPeer, filter: Api.MessagesFilter, offsetId: Int32, offsetDate: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1240514025) - peer.serialize(buffer, true) - filter.serialize(buffer, true) - serializeInt32(offsetId, buffer: buffer, boxed: false) - serializeInt32(offsetDate, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getSearchResultsCalendar", parameters: [("peer", String(describing: peer)), ("filter", String(describing: filter)), ("offsetId", String(describing: offsetId)), ("offsetDate", String(describing: offsetDate))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SearchResultsCalendar? in - let reader = BufferReader(buffer) - var result: Api.messages.SearchResultsCalendar? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.SearchResultsCalendar - } - return result - }) - } -} -public extension Api.functions.messages { - static func getSearchResultsPositions(peer: Api.InputPeer, filter: Api.MessagesFilter, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1855292323) - peer.serialize(buffer, true) - filter.serialize(buffer, true) - serializeInt32(offsetId, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getSearchResultsPositions", parameters: [("peer", String(describing: peer)), ("filter", String(describing: filter)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SearchResultsPositions? in - let reader = BufferReader(buffer) - var result: Api.messages.SearchResultsPositions? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.SearchResultsPositions - } - return result - }) - } -} -public extension Api.functions.messages { - static func getSplitRanges() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.MessageRange]>) { - let buffer = Buffer() - buffer.appendInt32(486505992) - - return (FunctionDescription(name: "messages.getSplitRanges", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.MessageRange]? in - let reader = BufferReader(buffer) - var result: [Api.MessageRange]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageRange.self) - } - return result - }) - } -} -public extension Api.functions.messages { - static func getStickerSet(stickerset: Api.InputStickerSet, hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-928977804) - stickerset.serialize(buffer, true) - serializeInt32(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getStickerSet", parameters: [("stickerset", String(describing: stickerset)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in - let reader = BufferReader(buffer) - var result: Api.messages.StickerSet? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.StickerSet - } - return result - }) - } -} -public extension Api.functions.messages { - static func getStickers(emoticon: String, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-710552671) - serializeString(emoticon, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getStickers", parameters: [("emoticon", String(describing: emoticon)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Stickers? in - let reader = BufferReader(buffer) - var result: Api.messages.Stickers? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Stickers - } - return result - }) - } -} -public extension Api.functions.messages { - static func getSuggestedDialogFilters() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.DialogFilterSuggested]>) { - let buffer = Buffer() - buffer.appendInt32(-1566780372) - - return (FunctionDescription(name: "messages.getSuggestedDialogFilters", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.DialogFilterSuggested]? in - let reader = BufferReader(buffer) - var result: [Api.DialogFilterSuggested]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.DialogFilterSuggested.self) - } - return result - }) - } -} -public extension Api.functions.messages { - static func getTopReactions(limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1149164102) - serializeInt32(limit, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getTopReactions", parameters: [("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Reactions? in - let reader = BufferReader(buffer) - var result: Api.messages.Reactions? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Reactions - } - return result - }) - } -} -public extension Api.functions.messages { - static func getUnreadMentions(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-251140208) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} - serializeInt32(offsetId, buffer: buffer, boxed: false) - serializeInt32(addOffset, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - serializeInt32(maxId, buffer: buffer, boxed: false) - serializeInt32(minId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getUnreadMentions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId)), ("offsetId", String(describing: offsetId)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in - let reader = BufferReader(buffer) - var result: Api.messages.Messages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Messages - } - return result - }) - } -} -public extension Api.functions.messages { - static func getUnreadReactions(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(841173339) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} - serializeInt32(offsetId, buffer: buffer, boxed: false) - serializeInt32(addOffset, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - serializeInt32(maxId, buffer: buffer, boxed: false) - serializeInt32(minId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getUnreadReactions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId)), ("offsetId", String(describing: offsetId)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in - let reader = BufferReader(buffer) - var result: Api.messages.Messages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Messages - } - return result - }) - } -} -public extension Api.functions.messages { - static func getWebPage(url: String, hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1919511901) - serializeString(url, buffer: buffer, boxed: false) - serializeInt32(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getWebPage", parameters: [("url", String(describing: url)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.WebPage? in - let reader = BufferReader(buffer) - var result: Api.messages.WebPage? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.WebPage - } - return result - }) - } -} -public extension Api.functions.messages { - static func getWebPagePreview(flags: Int32, message: String, entities: [Api.MessageEntity]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1956073268) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(message, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities!.count)) - for item in entities! { - item.serialize(buffer, true) - }} - return (FunctionDescription(name: "messages.getWebPagePreview", parameters: [("flags", String(describing: flags)), ("message", String(describing: message)), ("entities", String(describing: entities))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.MessageMedia? in - let reader = BufferReader(buffer) - var result: Api.MessageMedia? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.MessageMedia - } - return result - }) - } -} -public extension Api.functions.messages { - static func hideAllChatJoinRequests(flags: Int32, peer: Api.InputPeer, link: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-528091926) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 1) != 0 {serializeString(link!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "messages.hideAllChatJoinRequests", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("link", String(describing: link))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func hideChatJoinRequest(flags: Int32, peer: Api.InputPeer, userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2145904661) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - userId.serialize(buffer, true) - return (FunctionDescription(name: "messages.hideChatJoinRequest", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("userId", String(describing: userId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func hidePeerSettingsBar(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1336717624) - peer.serialize(buffer, true) - return (FunctionDescription(name: "messages.hidePeerSettingsBar", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func importChatInvite(hash: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1817183516) - serializeString(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.importChatInvite", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func initHistoryImport(peer: Api.InputPeer, file: Api.InputFile, mediaCount: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(873008187) - peer.serialize(buffer, true) - file.serialize(buffer, true) - serializeInt32(mediaCount, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.initHistoryImport", parameters: [("peer", String(describing: peer)), ("file", String(describing: file)), ("mediaCount", String(describing: mediaCount))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.HistoryImport? in - let reader = BufferReader(buffer) - var result: Api.messages.HistoryImport? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.HistoryImport - } - return result - }) - } -} -public extension Api.functions.messages { - static func installStickerSet(stickerset: Api.InputStickerSet, archived: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-946871200) - stickerset.serialize(buffer, true) - archived.serialize(buffer, true) - return (FunctionDescription(name: "messages.installStickerSet", parameters: [("stickerset", String(describing: stickerset)), ("archived", String(describing: archived))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSetInstallResult? in - let reader = BufferReader(buffer) - var result: Api.messages.StickerSetInstallResult? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.StickerSetInstallResult - } - return result - }) - } -} -public extension Api.functions.messages { - static func markDialogUnread(flags: Int32, peer: Api.InputDialogPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1031349873) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - return (FunctionDescription(name: "messages.markDialogUnread", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func migrateChat(chatId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1568189671) - serializeInt64(chatId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.migrateChat", parameters: [("chatId", String(describing: chatId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func prolongWebView(flags: Int32, peer: Api.InputPeer, bot: Api.InputUser, queryId: Int64, replyTo: Api.InputReplyTo?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1328014717) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - bot.serialize(buffer, true) - serializeInt64(queryId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {replyTo!.serialize(buffer, true)} - if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)} - return (FunctionDescription(name: "messages.prolongWebView", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("bot", String(describing: bot)), ("queryId", String(describing: queryId)), ("replyTo", String(describing: replyTo)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func rateTranscribedAudio(peer: Api.InputPeer, msgId: Int32, transcriptionId: Int64, good: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2132608815) - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - serializeInt64(transcriptionId, buffer: buffer, boxed: false) - good.serialize(buffer, true) - return (FunctionDescription(name: "messages.rateTranscribedAudio", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("transcriptionId", String(describing: transcriptionId)), ("good", String(describing: good))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func readDiscussion(peer: Api.InputPeer, msgId: Int32, readMaxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-147740172) - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - serializeInt32(readMaxId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.readDiscussion", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("readMaxId", String(describing: readMaxId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func readEncryptedHistory(peer: Api.InputEncryptedChat, maxDate: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2135648522) - peer.serialize(buffer, true) - serializeInt32(maxDate, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.readEncryptedHistory", parameters: [("peer", String(describing: peer)), ("maxDate", String(describing: maxDate))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func readFeaturedStickers(id: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1527873830) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt64(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "messages.readFeaturedStickers", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func readHistory(peer: Api.InputPeer, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(238054714) - peer.serialize(buffer, true) - serializeInt32(maxId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.readHistory", parameters: [("peer", String(describing: peer)), ("maxId", String(describing: maxId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedMessages? in - let reader = BufferReader(buffer) - var result: Api.messages.AffectedMessages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.AffectedMessages - } - return result - }) - } -} -public extension Api.functions.messages { - static func readMentions(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(921026381) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "messages.readMentions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedHistory? in - let reader = BufferReader(buffer) - var result: Api.messages.AffectedHistory? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.AffectedHistory - } - return result - }) - } -} -public extension Api.functions.messages { - static func readMessageContents(id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(916930423) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "messages.readMessageContents", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedMessages? in - let reader = BufferReader(buffer) - var result: Api.messages.AffectedMessages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.AffectedMessages - } - return result - }) - } -} -public extension Api.functions.messages { - static func readReactions(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1420459918) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "messages.readReactions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedHistory? in - let reader = BufferReader(buffer) - var result: Api.messages.AffectedHistory? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.AffectedHistory - } - return result - }) - } -} -public extension Api.functions.messages { - static func receivedMessages(maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.ReceivedNotifyMessage]>) { - let buffer = Buffer() - buffer.appendInt32(94983360) - serializeInt32(maxId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.receivedMessages", parameters: [("maxId", String(describing: maxId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.ReceivedNotifyMessage]? in - let reader = BufferReader(buffer) - var result: [Api.ReceivedNotifyMessage]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.ReceivedNotifyMessage.self) - } - return result - }) - } -} -public extension Api.functions.messages { - static func receivedQueue(maxQts: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int64]>) { - let buffer = Buffer() - buffer.appendInt32(1436924774) - serializeInt32(maxQts, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.receivedQueue", parameters: [("maxQts", String(describing: maxQts))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int64]? in - let reader = BufferReader(buffer) - var result: [Int64]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) - } - return result - }) - } -} -public extension Api.functions.messages { - static func reorderPinnedDialogs(flags: Int32, folderId: Int32, order: [Api.InputDialogPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(991616823) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(folderId, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(order.count)) - for item in order { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "messages.reorderPinnedDialogs", parameters: [("flags", String(describing: flags)), ("folderId", String(describing: folderId)), ("order", String(describing: order))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func reorderStickerSets(flags: Int32, order: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2016638777) - serializeInt32(flags, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(order.count)) - for item in order { - serializeInt64(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "messages.reorderStickerSets", parameters: [("flags", String(describing: flags)), ("order", String(describing: order))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func report(peer: Api.InputPeer, id: [Int32], reason: Api.ReportReason, message: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1991005362) - peer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) +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) } - reason.serialize(buffer, true) - serializeString(message, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.report", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("reason", String(describing: reason)), ("message", String(describing: message))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func reportEncryptedSpam(peer: Api.InputEncryptedChat) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1259113487) - peer.serialize(buffer, true) - return (FunctionDescription(name: "messages.reportEncryptedSpam", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func reportReaction(peer: Api.InputPeer, id: Int32, reactionPeer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1063567478) - peer.serialize(buffer, true) - serializeInt32(id, buffer: buffer, boxed: false) - reactionPeer.serialize(buffer, true) - return (FunctionDescription(name: "messages.reportReaction", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("reactionPeer", String(describing: reactionPeer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func reportSpam(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-820669733) - peer.serialize(buffer, true) - return (FunctionDescription(name: "messages.reportSpam", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func requestAppWebView(flags: Int32, peer: Api.InputPeer, app: Api.InputBotApp, startParam: String?, themeParams: Api.DataJSON?, platform: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1940243652) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - app.serialize(buffer, true) - if Int(flags) & Int(1 << 1) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {themeParams!.serialize(buffer, true)} - serializeString(platform, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.requestAppWebView", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("app", String(describing: app)), ("startParam", String(describing: startParam)), ("themeParams", String(describing: themeParams)), ("platform", String(describing: platform))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.AppWebViewResult? in - let reader = BufferReader(buffer) - var result: Api.AppWebViewResult? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.AppWebViewResult - } - return result - }) - } -} -public extension Api.functions.messages { - static func requestEncryption(userId: Api.InputUser, randomId: Int32, gA: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-162681021) - userId.serialize(buffer, true) - serializeInt32(randomId, buffer: buffer, boxed: false) - serializeBytes(gA, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.requestEncryption", parameters: [("userId", String(describing: userId)), ("randomId", String(describing: randomId)), ("gA", String(describing: gA))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EncryptedChat? in - let reader = BufferReader(buffer) - var result: Api.EncryptedChat? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.EncryptedChat - } - return result - }) - } -} -public extension Api.functions.messages { - static func requestSimpleWebView(flags: Int32, bot: Api.InputUser, url: String?, startParam: String?, themeParams: Api.DataJSON?, platform: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(440815626) - serializeInt32(flags, buffer: buffer, boxed: false) - bot.serialize(buffer, true) - if Int(flags) & Int(1 << 3) != 0 {serializeString(url!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 0) != 0 {themeParams!.serialize(buffer, true)} - serializeString(platform, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.requestSimpleWebView", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("url", String(describing: url)), ("startParam", String(describing: startParam)), ("themeParams", String(describing: themeParams)), ("platform", String(describing: platform))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.SimpleWebViewResult? in - let reader = BufferReader(buffer) - var result: Api.SimpleWebViewResult? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.SimpleWebViewResult - } - return result - }) - } -} -public extension Api.functions.messages { - static func requestUrlAuth(flags: Int32, peer: Api.InputPeer?, msgId: Int32?, buttonId: Int32?, url: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(428848198) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {peer!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(msgId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(buttonId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(url!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "messages.requestUrlAuth", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("buttonId", String(describing: buttonId)), ("url", String(describing: url))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.UrlAuthResult? in - let reader = BufferReader(buffer) - var result: Api.UrlAuthResult? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.UrlAuthResult - } - return result - }) - } -} -public extension Api.functions.messages { - static func requestWebView(flags: Int32, peer: Api.InputPeer, bot: Api.InputUser, url: String?, startParam: String?, themeParams: Api.DataJSON?, platform: String, replyTo: Api.InputReplyTo?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(647873217) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - bot.serialize(buffer, true) - if Int(flags) & Int(1 << 1) != 0 {serializeString(url!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {themeParams!.serialize(buffer, true)} - serializeString(platform, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {replyTo!.serialize(buffer, true)} - if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)} - return (FunctionDescription(name: "messages.requestWebView", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("bot", String(describing: bot)), ("url", String(describing: url)), ("startParam", String(describing: startParam)), ("themeParams", String(describing: themeParams)), ("platform", String(describing: platform)), ("replyTo", String(describing: replyTo)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.WebViewResult? in - let reader = BufferReader(buffer) - var result: Api.WebViewResult? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.WebViewResult - } - return result - }) - } -} -public extension Api.functions.messages { - static func saveDefaultSendAs(peer: Api.InputPeer, sendAs: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-855777386) - peer.serialize(buffer, true) - sendAs.serialize(buffer, true) - return (FunctionDescription(name: "messages.saveDefaultSendAs", parameters: [("peer", String(describing: peer)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func saveDraft(flags: Int32, replyTo: Api.InputReplyTo?, peer: Api.InputPeer, message: String, entities: [Api.MessageEntity]?, media: Api.InputMedia?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2146678790) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 4) != 0 {replyTo!.serialize(buffer, true)} - peer.serialize(buffer, true) - serializeString(message, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities!.count)) - for item in entities! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 5) != 0 {media!.serialize(buffer, true)} - return (FunctionDescription(name: "messages.saveDraft", parameters: [("flags", String(describing: flags)), ("replyTo", String(describing: replyTo)), ("peer", String(describing: peer)), ("message", String(describing: message)), ("entities", String(describing: entities)), ("media", String(describing: media))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func saveGif(id: Api.InputDocument, unsave: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(846868683) - id.serialize(buffer, true) - unsave.serialize(buffer, true) - return (FunctionDescription(name: "messages.saveGif", parameters: [("id", String(describing: id)), ("unsave", String(describing: unsave))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func saveRecentSticker(flags: Int32, id: Api.InputDocument, unsave: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(958863608) - serializeInt32(flags, buffer: buffer, boxed: false) - id.serialize(buffer, true) - unsave.serialize(buffer, true) - return (FunctionDescription(name: "messages.saveRecentSticker", parameters: [("flags", String(describing: flags)), ("id", String(describing: id)), ("unsave", String(describing: unsave))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func search(flags: Int32, peer: Api.InputPeer, q: String, fromId: Api.InputPeer?, topMsgId: Int32?, filter: Api.MessagesFilter, minDate: Int32, maxDate: Int32, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1593989278) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeString(q, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {fromId!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} - filter.serialize(buffer, true) - serializeInt32(minDate, buffer: buffer, boxed: false) - serializeInt32(maxDate, buffer: buffer, boxed: false) - serializeInt32(offsetId, buffer: buffer, boxed: false) - serializeInt32(addOffset, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - serializeInt32(maxId, buffer: buffer, boxed: false) - serializeInt32(minId, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.search", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("q", String(describing: q)), ("fromId", String(describing: fromId)), ("topMsgId", String(describing: topMsgId)), ("filter", String(describing: filter)), ("minDate", String(describing: minDate)), ("maxDate", String(describing: maxDate)), ("offsetId", String(describing: offsetId)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in - let reader = BufferReader(buffer) - var result: Api.messages.Messages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Messages - } - return result - }) - } -} -public extension Api.functions.messages { - static func searchCustomEmoji(emoticon: String, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(739360983) - serializeString(emoticon, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.searchCustomEmoji", parameters: [("emoticon", String(describing: emoticon)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EmojiList? in - let reader = BufferReader(buffer) - var result: Api.EmojiList? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.EmojiList - } - return result - }) - } -} -public extension Api.functions.messages { - static func searchGlobal(flags: Int32, folderId: Int32?, q: String, filter: Api.MessagesFilter, minDate: Int32, maxDate: Int32, offsetRate: Int32, offsetPeer: Api.InputPeer, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1271290010) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} - serializeString(q, buffer: buffer, boxed: false) - filter.serialize(buffer, true) - serializeInt32(minDate, buffer: buffer, boxed: false) - serializeInt32(maxDate, buffer: buffer, boxed: false) - serializeInt32(offsetRate, buffer: buffer, boxed: false) - offsetPeer.serialize(buffer, true) - serializeInt32(offsetId, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.searchGlobal", parameters: [("flags", String(describing: flags)), ("folderId", String(describing: folderId)), ("q", String(describing: q)), ("filter", String(describing: filter)), ("minDate", String(describing: minDate)), ("maxDate", String(describing: maxDate)), ("offsetRate", String(describing: offsetRate)), ("offsetPeer", String(describing: offsetPeer)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in - let reader = BufferReader(buffer) - var result: Api.messages.Messages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Messages - } - return result - }) - } -} -public extension Api.functions.messages { - static func searchSentMedia(q: String, filter: Api.MessagesFilter, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(276705696) - serializeString(q, buffer: buffer, boxed: false) - filter.serialize(buffer, true) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.searchSentMedia", parameters: [("q", String(describing: q)), ("filter", String(describing: filter)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in - let reader = BufferReader(buffer) - var result: Api.messages.Messages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Messages - } - return result - }) - } -} -public extension Api.functions.messages { - static func searchStickerSets(flags: Int32, q: String, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(896555914) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(q, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.searchStickerSets", parameters: [("flags", String(describing: flags)), ("q", String(describing: q)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.FoundStickerSets? in - let reader = BufferReader(buffer) - var result: Api.messages.FoundStickerSets? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.FoundStickerSets - } - return result - }) - } -} -public extension Api.functions.messages { - static func sendBotRequestedPeer(peer: Api.InputPeer, msgId: Int32, buttonId: Int32, requestedPeer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-29831141) - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - serializeInt32(buttonId, buffer: buffer, boxed: false) - requestedPeer.serialize(buffer, true) - return (FunctionDescription(name: "messages.sendBotRequestedPeer", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("buttonId", String(describing: buttonId)), ("requestedPeer", String(describing: requestedPeer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func sendEncrypted(flags: Int32, peer: Api.InputEncryptedChat, randomId: Int64, data: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1157265941) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt64(randomId, buffer: buffer, boxed: false) - serializeBytes(data, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.sendEncrypted", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("randomId", String(describing: randomId)), ("data", String(describing: data))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SentEncryptedMessage? in - let reader = BufferReader(buffer) - var result: Api.messages.SentEncryptedMessage? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.SentEncryptedMessage - } - return result - }) - } -} -public extension Api.functions.messages { - static func sendEncryptedFile(flags: Int32, peer: Api.InputEncryptedChat, randomId: Int64, data: Buffer, file: Api.InputEncryptedFile) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1431914525) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt64(randomId, buffer: buffer, boxed: false) - serializeBytes(data, buffer: buffer, boxed: false) - file.serialize(buffer, true) - return (FunctionDescription(name: "messages.sendEncryptedFile", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("randomId", String(describing: randomId)), ("data", String(describing: data)), ("file", String(describing: file))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SentEncryptedMessage? in - let reader = BufferReader(buffer) - var result: Api.messages.SentEncryptedMessage? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.SentEncryptedMessage - } - return result - }) - } -} -public extension Api.functions.messages { - static func sendEncryptedService(peer: Api.InputEncryptedChat, randomId: Int64, data: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(852769188) - peer.serialize(buffer, true) - serializeInt64(randomId, buffer: buffer, boxed: false) - serializeBytes(data, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.sendEncryptedService", parameters: [("peer", String(describing: peer)), ("randomId", String(describing: randomId)), ("data", String(describing: data))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SentEncryptedMessage? in - let reader = BufferReader(buffer) - var result: Api.messages.SentEncryptedMessage? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.SentEncryptedMessage - } - return result - }) - } -} -public extension Api.functions.messages { - static func sendInlineBotResult(flags: Int32, peer: Api.InputPeer, replyTo: Api.InputReplyTo?, randomId: Int64, queryId: Int64, id: String, scheduleDate: Int32?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-138647366) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {replyTo!.serialize(buffer, true)} - serializeInt64(randomId, buffer: buffer, boxed: false) - serializeInt64(queryId, buffer: buffer, boxed: false) - serializeString(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)} - return (FunctionDescription(name: "messages.sendInlineBotResult", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("randomId", String(describing: randomId)), ("queryId", String(describing: queryId)), ("id", String(describing: id)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func sendMedia(flags: Int32, peer: Api.InputPeer, replyTo: Api.InputReplyTo?, media: Api.InputMedia, message: String, randomId: Int64, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1926021693) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {replyTo!.serialize(buffer, true)} - media.serialize(buffer, true) - serializeString(message, buffer: buffer, boxed: false) - serializeInt64(randomId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} - if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities!.count)) - for item in entities! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)} - return (FunctionDescription(name: "messages.sendMedia", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("media", String(describing: media)), ("message", String(describing: message)), ("randomId", String(describing: randomId)), ("replyMarkup", String(describing: replyMarkup)), ("entities", String(describing: entities)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func sendMessage(flags: Int32, peer: Api.InputPeer, replyTo: Api.InputReplyTo?, message: String, randomId: Int64, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(671943023) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {replyTo!.serialize(buffer, true)} - serializeString(message, buffer: buffer, boxed: false) - serializeInt64(randomId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} - if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities!.count)) - for item in entities! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)} - return (FunctionDescription(name: "messages.sendMessage", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("message", String(describing: message)), ("randomId", String(describing: randomId)), ("replyMarkup", String(describing: replyMarkup)), ("entities", String(describing: entities)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func sendMultiMedia(flags: Int32, peer: Api.InputPeer, replyTo: Api.InputReplyTo?, multiMedia: [Api.InputSingleMedia], scheduleDate: Int32?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1164872071) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {replyTo!.serialize(buffer, true)} buffer.appendInt32(481674261) - buffer.appendInt32(Int32(multiMedia.count)) - for item in multiMedia { - item.serialize(buffer, true) - } - if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)} - return (FunctionDescription(name: "messages.sendMultiMedia", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("multiMedia", String(describing: multiMedia)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func sendReaction(flags: Int32, peer: Api.InputPeer, msgId: Int32, reaction: [Api.Reaction]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-754091820) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(reaction!.count)) - for item in reaction! { + buffer.appendInt32(Int32(newMessages.count)) + for item in newMessages { item.serialize(buffer, true) - }} - return (FunctionDescription(name: "messages.sendReaction", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("reaction", String(describing: reaction))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func sendScheduledMessages(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1120369398) - peer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "messages.sendScheduledMessages", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func sendScreenshotNotification(peer: Api.InputPeer, replyTo: Api.InputReplyTo, randomId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1589618665) - peer.serialize(buffer, true) - replyTo.serialize(buffer, true) - serializeInt64(randomId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.sendScreenshotNotification", parameters: [("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("randomId", String(describing: randomId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func sendVote(peer: Api.InputPeer, msgId: Int32, options: [Buffer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(283795844) - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(options.count)) - for item in options { - serializeBytes(item, buffer: buffer, boxed: false) } - return (FunctionDescription(name: "messages.sendVote", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("options", String(describing: options))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func sendWebViewData(bot: Api.InputUser, randomId: Int64, buttonText: String, data: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-603831608) - bot.serialize(buffer, true) - serializeInt64(randomId, buffer: buffer, boxed: false) - serializeString(buttonText, buffer: buffer, boxed: false) - serializeString(data, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.sendWebViewData", parameters: [("bot", String(describing: bot)), ("randomId", String(describing: randomId)), ("buttonText", String(describing: buttonText)), ("data", String(describing: data))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func sendWebViewResultMessage(botQueryId: String, result: Api.InputBotInlineResult) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(172168437) - serializeString(botQueryId, buffer: buffer, boxed: false) - result.serialize(buffer, true) - return (FunctionDescription(name: "messages.sendWebViewResultMessage", parameters: [("botQueryId", String(describing: botQueryId)), ("result", String(describing: result))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.WebViewMessageSent? in - let reader = BufferReader(buffer) - var result: Api.WebViewMessageSent? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.WebViewMessageSent - } - return result - }) - } -} -public extension Api.functions.messages { - static func setBotCallbackAnswer(flags: Int32, queryId: Int64, message: String?, url: String?, cacheTime: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-712043766) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(queryId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(message!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(url!, buffer: buffer, boxed: false)} - serializeInt32(cacheTime, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.setBotCallbackAnswer", parameters: [("flags", String(describing: flags)), ("queryId", String(describing: queryId)), ("message", String(describing: message)), ("url", String(describing: url)), ("cacheTime", String(describing: cacheTime))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func setBotPrecheckoutResults(flags: Int32, queryId: Int64, error: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(163765653) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(queryId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(error!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "messages.setBotPrecheckoutResults", parameters: [("flags", String(describing: flags)), ("queryId", String(describing: queryId)), ("error", String(describing: error))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func setBotShippingResults(flags: Int32, queryId: Int64, error: String?, shippingOptions: [Api.ShippingOption]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-436833542) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(queryId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(error!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(shippingOptions!.count)) - for item in shippingOptions! { - item.serialize(buffer, true) - }} - return (FunctionDescription(name: "messages.setBotShippingResults", parameters: [("flags", String(describing: flags)), ("queryId", String(describing: queryId)), ("error", String(describing: error)), ("shippingOptions", String(describing: shippingOptions))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func setChatAvailableReactions(peer: Api.InputPeer, availableReactions: Api.ChatReactions) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-21928079) - peer.serialize(buffer, true) - availableReactions.serialize(buffer, true) - return (FunctionDescription(name: "messages.setChatAvailableReactions", parameters: [("peer", String(describing: peer)), ("availableReactions", String(describing: availableReactions))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func setChatTheme(peer: Api.InputPeer, emoticon: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-432283329) - 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 - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func setChatWallPaper(flags: Int32, peer: Api.InputPeer, wallpaper: Api.InputWallPaper?, settings: Api.WallPaperSettings?, id: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1879389471) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {wallpaper!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {settings!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(id!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "messages.setChatWallPaper", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("wallpaper", String(describing: wallpaper)), ("settings", String(describing: settings)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func setDefaultHistoryTTL(period: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1632299963) - serializeInt32(period, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.setDefaultHistoryTTL", parameters: [("period", String(describing: period))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func setDefaultReaction(reaction: Api.Reaction) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1330094102) - reaction.serialize(buffer, true) - return (FunctionDescription(name: "messages.setDefaultReaction", parameters: [("reaction", String(describing: reaction))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func setEncryptedTyping(peer: Api.InputEncryptedChat, typing: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2031374829) - peer.serialize(buffer, true) - typing.serialize(buffer, true) - return (FunctionDescription(name: "messages.setEncryptedTyping", parameters: [("peer", String(describing: peer)), ("typing", String(describing: typing))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func setGameScore(flags: Int32, peer: Api.InputPeer, id: Int32, userId: Api.InputUser, score: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1896289088) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(id, buffer: buffer, boxed: false) - userId.serialize(buffer, true) - serializeInt32(score, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.setGameScore", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id)), ("userId", String(describing: userId)), ("score", String(describing: score))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func setHistoryTTL(peer: Api.InputPeer, period: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1207017500) - peer.serialize(buffer, true) - serializeInt32(period, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.setHistoryTTL", parameters: [("peer", String(describing: peer)), ("period", String(describing: period))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func setInlineBotResults(flags: Int32, queryId: Int64, results: [Api.InputBotInlineResult], cacheTime: Int32, nextOffset: String?, switchPm: Api.InlineBotSwitchPM?, switchWebview: Api.InlineBotWebView?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1156406247) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(queryId, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(results.count)) - for item in results { + buffer.appendInt32(Int32(newEncryptedMessages.count)) + for item in newEncryptedMessages { item.serialize(buffer, true) } - serializeInt32(cacheTime, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {switchPm!.serialize(buffer, true)} - if Int(flags) & Int(1 << 4) != 0 {switchWebview!.serialize(buffer, true)} - return (FunctionDescription(name: "messages.setInlineBotResults", parameters: [("flags", String(describing: flags)), ("queryId", String(describing: queryId)), ("results", String(describing: results)), ("cacheTime", String(describing: cacheTime)), ("nextOffset", String(describing: nextOffset)), ("switchPm", String(describing: switchPm)), ("switchWebview", String(describing: switchWebview))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func setInlineGameScore(flags: Int32, id: Api.InputBotInlineMessageID, userId: Api.InputUser, score: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(363700068) - serializeInt32(flags, buffer: buffer, boxed: false) - id.serialize(buffer, true) - userId.serialize(buffer, true) - serializeInt32(score, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.setInlineGameScore", parameters: [("flags", String(describing: flags)), ("id", String(describing: id)), ("userId", String(describing: userId)), ("score", String(describing: score))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func setTyping(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?, action: Api.SendMessageAction) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1486110434) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} - action.serialize(buffer, true) - return (FunctionDescription(name: "messages.setTyping", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId)), ("action", String(describing: action))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func startBot(bot: Api.InputUser, peer: Api.InputPeer, randomId: Int64, startParam: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-421563528) - bot.serialize(buffer, true) - peer.serialize(buffer, true) - serializeInt64(randomId, buffer: buffer, boxed: false) - serializeString(startParam, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.startBot", parameters: [("bot", String(describing: bot)), ("peer", String(describing: peer)), ("randomId", String(describing: randomId)), ("startParam", String(describing: startParam))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func startHistoryImport(peer: Api.InputPeer, importId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1271008444) - peer.serialize(buffer, true) - serializeInt64(importId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.startHistoryImport", parameters: [("peer", String(describing: peer)), ("importId", String(describing: importId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func toggleBotInAttachMenu(flags: Int32, bot: Api.InputUser, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1777704297) - serializeInt32(flags, buffer: buffer, boxed: false) - bot.serialize(buffer, true) - enabled.serialize(buffer, true) - return (FunctionDescription(name: "messages.toggleBotInAttachMenu", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func toggleDialogPin(flags: Int32, peer: Api.InputDialogPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1489903017) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - return (FunctionDescription(name: "messages.toggleDialogPin", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func toggleNoForwards(peer: Api.InputPeer, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1323389022) - peer.serialize(buffer, true) - enabled.serialize(buffer, true) - return (FunctionDescription(name: "messages.toggleNoForwards", parameters: [("peer", String(describing: peer)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func togglePeerTranslations(flags: Int32, peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-461589127) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - return (FunctionDescription(name: "messages.togglePeerTranslations", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func toggleStickerSets(flags: Int32, stickersets: [Api.InputStickerSet]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1257951254) - serializeInt32(flags, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(stickersets.count)) - for item in stickersets { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "messages.toggleStickerSets", parameters: [("flags", String(describing: flags)), ("stickersets", String(describing: stickersets))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func transcribeAudio(peer: Api.InputPeer, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(647928393) - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.transcribeAudio", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.TranscribedAudio? in - let reader = BufferReader(buffer) - var result: Api.messages.TranscribedAudio? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.TranscribedAudio - } - return result - }) - } -} -public extension Api.functions.messages { - static func translateText(flags: Int32, peer: Api.InputPeer?, id: [Int32]?, text: [Api.TextWithEntities]?, toLang: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1662529584) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {peer!.serialize(buffer, true)} - if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id!.count)) - for item in id! { - serializeInt32(item, buffer: buffer, boxed: false) - }} - if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(text!.count)) - for item in text! { + buffer.appendInt32(Int32(otherUpdates.count)) + for item in otherUpdates { item.serialize(buffer, true) - }} - serializeString(toLang, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.translateText", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id)), ("text", String(describing: text)), ("toLang", String(describing: toLang))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.TranslatedText? in - let reader = BufferReader(buffer) - var result: Api.messages.TranslatedText? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.TranslatedText - } - return result - }) - } -} -public extension Api.functions.messages { - static func uninstallStickerSet(stickerset: Api.InputStickerSet) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-110209570) - stickerset.serialize(buffer, true) - return (FunctionDescription(name: "messages.uninstallStickerSet", parameters: [("stickerset", String(describing: stickerset))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func unpinAllMessages(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-299714136) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "messages.unpinAllMessages", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedHistory? in - let reader = BufferReader(buffer) - var result: Api.messages.AffectedHistory? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.AffectedHistory - } - return result - }) - } -} -public extension Api.functions.messages { - static func updateDialogFilter(flags: Int32, id: Int32, filter: Api.DialogFilter?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(450142282) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {filter!.serialize(buffer, true)} - return (FunctionDescription(name: "messages.updateDialogFilter", parameters: [("flags", String(describing: flags)), ("id", String(describing: id)), ("filter", String(describing: filter))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func updateDialogFiltersOrder(order: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-983318044) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(order.count)) - for item in order { - serializeInt32(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "messages.updateDialogFiltersOrder", parameters: [("order", String(describing: order))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} -public extension Api.functions.messages { - static func updatePinnedMessage(flags: Int32, peer: Api.InputPeer, id: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-760547348) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(id, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.updatePinnedMessage", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.messages { - static func uploadEncryptedFile(peer: Api.InputEncryptedChat, file: Api.InputEncryptedFile) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1347929239) - peer.serialize(buffer, true) - file.serialize(buffer, true) - return (FunctionDescription(name: "messages.uploadEncryptedFile", parameters: [("peer", String(describing: peer)), ("file", String(describing: file))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EncryptedFile? in - let reader = BufferReader(buffer) - var result: Api.EncryptedFile? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.EncryptedFile - } - return result - }) - } -} -public extension Api.functions.messages { - static func uploadImportedMedia(peer: Api.InputPeer, importId: Int64, fileName: String, media: Api.InputMedia) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(713433234) - peer.serialize(buffer, true) - serializeInt64(importId, buffer: buffer, boxed: false) - serializeString(fileName, buffer: buffer, boxed: false) - media.serialize(buffer, true) - return (FunctionDescription(name: "messages.uploadImportedMedia", parameters: [("peer", String(describing: peer)), ("importId", String(describing: importId)), ("fileName", String(describing: fileName)), ("media", String(describing: media))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.MessageMedia? in - let reader = BufferReader(buffer) - var result: Api.MessageMedia? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.MessageMedia - } - return result - }) - } -} -public extension Api.functions.messages { - static func uploadMedia(peer: Api.InputPeer, media: Api.InputMedia) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1369162417) - peer.serialize(buffer, true) - media.serialize(buffer, true) - return (FunctionDescription(name: "messages.uploadMedia", parameters: [("peer", String(describing: peer)), ("media", String(describing: media))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.MessageMedia? in - let reader = BufferReader(buffer) - var result: Api.MessageMedia? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.MessageMedia - } - return result - }) - } -} -public extension Api.functions.payments { - static func applyGiftCode(slug: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-152934316) - serializeString(slug, buffer: buffer, boxed: false) - return (FunctionDescription(name: "payments.applyGiftCode", parameters: [("slug", String(describing: slug))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.payments { - static func assignAppStoreTransaction(receipt: Buffer, purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-2131921795) - serializeBytes(receipt, buffer: buffer, boxed: false) - purpose.serialize(buffer, true) - return (FunctionDescription(name: "payments.assignAppStoreTransaction", parameters: [("receipt", String(describing: receipt)), ("purpose", String(describing: purpose))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.payments { - static func assignPlayMarketTransaction(receipt: Api.DataJSON, purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-537046829) - receipt.serialize(buffer, true) - purpose.serialize(buffer, true) - return (FunctionDescription(name: "payments.assignPlayMarketTransaction", parameters: [("receipt", String(describing: receipt)), ("purpose", String(describing: purpose))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.payments { - static func canPurchasePremium(purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1614700874) - purpose.serialize(buffer, true) - return (FunctionDescription(name: "payments.canPurchasePremium", parameters: [("purpose", String(describing: purpose))]), 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.payments { - static func checkGiftCode(slug: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1907247935) - serializeString(slug, buffer: buffer, boxed: false) - return (FunctionDescription(name: "payments.checkGiftCode", parameters: [("slug", String(describing: slug))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.CheckedGiftCode? in - let reader = BufferReader(buffer) - var result: Api.payments.CheckedGiftCode? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.payments.CheckedGiftCode - } - return result - }) - } -} -public extension Api.functions.payments { - static func clearSavedInfo(flags: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-667062079) - serializeInt32(flags, buffer: buffer, boxed: false) - return (FunctionDescription(name: "payments.clearSavedInfo", parameters: [("flags", String(describing: flags))]), 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.payments { - static func exportInvoice(invoiceMedia: Api.InputMedia) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(261206117) - invoiceMedia.serialize(buffer, true) - return (FunctionDescription(name: "payments.exportInvoice", parameters: [("invoiceMedia", String(describing: invoiceMedia))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.ExportedInvoice? in - let reader = BufferReader(buffer) - var result: Api.payments.ExportedInvoice? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.payments.ExportedInvoice - } - return result - }) - } -} -public extension Api.functions.payments { - static func getBankCardData(number: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(779736953) - serializeString(number, buffer: buffer, boxed: false) - return (FunctionDescription(name: "payments.getBankCardData", parameters: [("number", String(describing: number))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.BankCardData? in - let reader = BufferReader(buffer) - var result: Api.payments.BankCardData? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.payments.BankCardData - } - return result - }) - } -} -public extension Api.functions.payments { - static func getGiveawayInfo(peer: Api.InputPeer, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-198994907) - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "payments.getGiveawayInfo", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.GiveawayInfo? in - let reader = BufferReader(buffer) - var result: Api.payments.GiveawayInfo? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.payments.GiveawayInfo - } - return result - }) - } -} -public extension Api.functions.payments { - static func getPaymentForm(flags: Int32, invoice: Api.InputInvoice, themeParams: Api.DataJSON?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(924093883) - serializeInt32(flags, buffer: buffer, boxed: false) - invoice.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {themeParams!.serialize(buffer, true)} - return (FunctionDescription(name: "payments.getPaymentForm", parameters: [("flags", String(describing: flags)), ("invoice", String(describing: invoice)), ("themeParams", String(describing: themeParams))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.PaymentForm? in - let reader = BufferReader(buffer) - var result: Api.payments.PaymentForm? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.payments.PaymentForm - } - return result - }) - } -} -public extension Api.functions.payments { - static func getPaymentReceipt(peer: Api.InputPeer, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(611897804) - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "payments.getPaymentReceipt", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.PaymentReceipt? in - let reader = BufferReader(buffer) - var result: Api.payments.PaymentReceipt? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.payments.PaymentReceipt - } - return result - }) - } -} -public extension Api.functions.payments { - static func getPremiumGiftCodeOptions(flags: Int32, boostPeer: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.PremiumGiftCodeOption]>) { - let buffer = Buffer() - buffer.appendInt32(660060756) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {boostPeer!.serialize(buffer, true)} - return (FunctionDescription(name: "payments.getPremiumGiftCodeOptions", parameters: [("flags", String(describing: flags)), ("boostPeer", String(describing: boostPeer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.PremiumGiftCodeOption]? in - let reader = BufferReader(buffer) - var result: [Api.PremiumGiftCodeOption]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.PremiumGiftCodeOption.self) - } - return result - }) - } -} -public extension Api.functions.payments { - static func getSavedInfo() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(578650699) - - return (FunctionDescription(name: "payments.getSavedInfo", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.SavedInfo? in - let reader = BufferReader(buffer) - var result: Api.payments.SavedInfo? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.payments.SavedInfo - } - return result - }) - } -} -public extension Api.functions.payments { - static func launchPrepaidGiveaway(peer: Api.InputPeer, giveawayId: Int64, purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1609928480) - peer.serialize(buffer, true) - serializeInt64(giveawayId, buffer: buffer, boxed: false) - purpose.serialize(buffer, true) - return (FunctionDescription(name: "payments.launchPrepaidGiveaway", parameters: [("peer", String(describing: peer)), ("giveawayId", String(describing: giveawayId)), ("purpose", String(describing: purpose))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.payments { - static func sendPaymentForm(flags: Int32, formId: Int64, invoice: Api.InputInvoice, requestedInfoId: String?, shippingOptionId: String?, credentials: Api.InputPaymentCredentials, tipAmount: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(755192367) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(formId, buffer: buffer, boxed: false) - invoice.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {serializeString(requestedInfoId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeString(shippingOptionId!, buffer: buffer, boxed: false)} - credentials.serialize(buffer, true) - if Int(flags) & Int(1 << 2) != 0 {serializeInt64(tipAmount!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "payments.sendPaymentForm", parameters: [("flags", String(describing: flags)), ("formId", String(describing: formId)), ("invoice", String(describing: invoice)), ("requestedInfoId", String(describing: requestedInfoId)), ("shippingOptionId", String(describing: shippingOptionId)), ("credentials", String(describing: credentials)), ("tipAmount", String(describing: tipAmount))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.PaymentResult? in - let reader = BufferReader(buffer) - var result: Api.payments.PaymentResult? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.payments.PaymentResult - } - return result - }) - } -} -public extension Api.functions.payments { - static func validateRequestedInfo(flags: Int32, invoice: Api.InputInvoice, info: Api.PaymentRequestedInfo) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1228345045) - serializeInt32(flags, buffer: buffer, boxed: false) - invoice.serialize(buffer, true) - info.serialize(buffer, true) - return (FunctionDescription(name: "payments.validateRequestedInfo", parameters: [("flags", String(describing: flags)), ("invoice", String(describing: invoice)), ("info", String(describing: info))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.ValidatedRequestedInfo? in - let reader = BufferReader(buffer) - var result: Api.payments.ValidatedRequestedInfo? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.payments.ValidatedRequestedInfo - } - return result - }) - } -} -public extension Api.functions.phone { - static func acceptCall(peer: Api.InputPhoneCall, gB: Buffer, `protocol`: Api.PhoneCallProtocol) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1003664544) - peer.serialize(buffer, true) - serializeBytes(gB, buffer: buffer, boxed: false) - `protocol`.serialize(buffer, true) - return (FunctionDescription(name: "phone.acceptCall", parameters: [("peer", String(describing: peer)), ("gB", String(describing: gB)), ("`protocol`", String(describing: `protocol`))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.PhoneCall? in - let reader = BufferReader(buffer) - var result: Api.phone.PhoneCall? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.phone.PhoneCall - } - return result - }) - } -} -public extension Api.functions.phone { - static func checkGroupCall(call: Api.InputGroupCall, sources: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int32]>) { - let buffer = Buffer() - buffer.appendInt32(-1248003721) - call.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(sources.count)) - for item in sources { - serializeInt32(item, buffer: buffer, boxed: false) } - return (FunctionDescription(name: "phone.checkGroupCall", parameters: [("call", String(describing: call)), ("sources", String(describing: sources))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int32]? in - let reader = BufferReader(buffer) - var result: [Int32]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - return result - }) - } -} -public extension Api.functions.phone { - static func confirmCall(peer: Api.InputPhoneCall, gA: Buffer, keyFingerprint: Int64, `protocol`: Api.PhoneCallProtocol) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(788404002) - peer.serialize(buffer, true) - serializeBytes(gA, buffer: buffer, boxed: false) - serializeInt64(keyFingerprint, buffer: buffer, boxed: false) - `protocol`.serialize(buffer, true) - return (FunctionDescription(name: "phone.confirmCall", parameters: [("peer", String(describing: peer)), ("gA", String(describing: gA)), ("keyFingerprint", String(describing: keyFingerprint)), ("`protocol`", String(describing: `protocol`))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.PhoneCall? in - let reader = BufferReader(buffer) - var result: Api.phone.PhoneCall? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.phone.PhoneCall - } - return result - }) - } -} -public extension Api.functions.phone { - static func createGroupCall(flags: Int32, peer: Api.InputPeer, randomId: Int32, title: String?, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1221445336) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(randomId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "phone.createGroupCall", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("randomId", String(describing: randomId)), ("title", String(describing: title)), ("scheduleDate", String(describing: scheduleDate))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.phone { - static func discardCall(flags: Int32, peer: Api.InputPhoneCall, duration: Int32, reason: Api.PhoneCallDiscardReason, connectionId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1295269440) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(duration, buffer: buffer, boxed: false) - reason.serialize(buffer, true) - serializeInt64(connectionId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "phone.discardCall", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("duration", String(describing: duration)), ("reason", String(describing: reason)), ("connectionId", String(describing: connectionId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.phone { - static func discardGroupCall(call: Api.InputGroupCall) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2054648117) - call.serialize(buffer, true) - return (FunctionDescription(name: "phone.discardGroupCall", parameters: [("call", String(describing: call))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.phone { - static func editGroupCallParticipant(flags: Int32, call: Api.InputGroupCall, participant: Api.InputPeer, muted: Api.Bool?, volume: Int32?, raiseHand: Api.Bool?, videoStopped: Api.Bool?, videoPaused: Api.Bool?, presentationPaused: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1524155713) - serializeInt32(flags, buffer: buffer, boxed: false) - call.serialize(buffer, true) - participant.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {muted!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(volume!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {raiseHand!.serialize(buffer, true)} - if Int(flags) & Int(1 << 3) != 0 {videoStopped!.serialize(buffer, true)} - if Int(flags) & Int(1 << 4) != 0 {videoPaused!.serialize(buffer, true)} - if Int(flags) & Int(1 << 5) != 0 {presentationPaused!.serialize(buffer, true)} - return (FunctionDescription(name: "phone.editGroupCallParticipant", parameters: [("flags", String(describing: flags)), ("call", String(describing: call)), ("participant", String(describing: participant)), ("muted", String(describing: muted)), ("volume", String(describing: volume)), ("raiseHand", String(describing: raiseHand)), ("videoStopped", String(describing: videoStopped)), ("videoPaused", String(describing: videoPaused)), ("presentationPaused", String(describing: presentationPaused))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.phone { - static func editGroupCallTitle(call: Api.InputGroupCall, title: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(480685066) - call.serialize(buffer, true) - serializeString(title, buffer: buffer, boxed: false) - return (FunctionDescription(name: "phone.editGroupCallTitle", parameters: [("call", String(describing: call)), ("title", String(describing: title))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.phone { - static func exportGroupCallInvite(flags: Int32, call: Api.InputGroupCall) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-425040769) - serializeInt32(flags, buffer: buffer, boxed: false) - call.serialize(buffer, true) - return (FunctionDescription(name: "phone.exportGroupCallInvite", parameters: [("flags", String(describing: flags)), ("call", String(describing: call))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.ExportedGroupCallInvite? in - let reader = BufferReader(buffer) - var result: Api.phone.ExportedGroupCallInvite? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.phone.ExportedGroupCallInvite - } - return result - }) - } -} -public extension Api.functions.phone { - static func getCallConfig() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1430593449) - - return (FunctionDescription(name: "phone.getCallConfig", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.DataJSON? in - let reader = BufferReader(buffer) - var result: Api.DataJSON? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.DataJSON - } - return result - }) - } -} -public extension Api.functions.phone { - static func getGroupCall(call: Api.InputGroupCall, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(68699611) - call.serialize(buffer, true) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "phone.getGroupCall", parameters: [("call", String(describing: call)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.GroupCall? in - let reader = BufferReader(buffer) - var result: Api.phone.GroupCall? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.phone.GroupCall - } - return result - }) - } -} -public extension Api.functions.phone { - static func getGroupCallJoinAs(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-277077702) - peer.serialize(buffer, true) - return (FunctionDescription(name: "phone.getGroupCallJoinAs", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.JoinAsPeers? in - let reader = BufferReader(buffer) - var result: Api.phone.JoinAsPeers? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.phone.JoinAsPeers - } - return result - }) - } -} -public extension Api.functions.phone { - static func getGroupCallStreamChannels(call: Api.InputGroupCall) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(447879488) - call.serialize(buffer, true) - return (FunctionDescription(name: "phone.getGroupCallStreamChannels", parameters: [("call", String(describing: call))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.GroupCallStreamChannels? in - let reader = BufferReader(buffer) - var result: Api.phone.GroupCallStreamChannels? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.phone.GroupCallStreamChannels - } - return result - }) - } -} -public extension Api.functions.phone { - static func getGroupCallStreamRtmpUrl(peer: Api.InputPeer, revoke: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-558650433) - peer.serialize(buffer, true) - revoke.serialize(buffer, true) - return (FunctionDescription(name: "phone.getGroupCallStreamRtmpUrl", parameters: [("peer", String(describing: peer)), ("revoke", String(describing: revoke))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.GroupCallStreamRtmpUrl? in - let reader = BufferReader(buffer) - var result: Api.phone.GroupCallStreamRtmpUrl? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.phone.GroupCallStreamRtmpUrl - } - return result - }) - } -} -public extension Api.functions.phone { - static func getGroupParticipants(call: Api.InputGroupCall, ids: [Api.InputPeer], sources: [Int32], offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-984033109) - call.serialize(buffer, true) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(ids.count)) - for item in ids { + buffer.appendInt32(Int32(chats.count)) + for item in chats { item.serialize(buffer, true) } buffer.appendInt32(481674261) - buffer.appendInt32(Int32(sources.count)) - for item in sources { - serializeInt32(item, buffer: buffer, boxed: false) - } - serializeString(offset, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "phone.getGroupParticipants", parameters: [("call", String(describing: call)), ("ids", String(describing: ids)), ("sources", String(describing: sources)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.GroupParticipants? in - let reader = BufferReader(buffer) - var result: Api.phone.GroupParticipants? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.phone.GroupParticipants - } - return result - }) - } -} -public extension Api.functions.phone { - static func inviteToGroupCall(call: Api.InputGroupCall, users: [Api.InputUser]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2067345760) - call.serialize(buffer, true) - buffer.appendInt32(481674261) buffer.appendInt32(Int32(users.count)) for item in users { item.serialize(buffer, true) } - return (FunctionDescription(name: "phone.inviteToGroupCall", parameters: [("call", String(describing: call)), ("users", String(describing: users))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.phone { - static func joinGroupCall(flags: Int32, call: Api.InputGroupCall, joinAs: Api.InputPeer, inviteHash: String?, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1322057861) - serializeInt32(flags, buffer: buffer, boxed: false) - call.serialize(buffer, true) - joinAs.serialize(buffer, true) - if Int(flags) & Int(1 << 1) != 0 {serializeString(inviteHash!, buffer: buffer, boxed: false)} - params.serialize(buffer, true) - return (FunctionDescription(name: "phone.joinGroupCall", parameters: [("flags", String(describing: flags)), ("call", String(describing: call)), ("joinAs", String(describing: joinAs)), ("inviteHash", String(describing: inviteHash)), ("params", String(describing: params))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.phone { - static func joinGroupCallPresentation(call: Api.InputGroupCall, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-873829436) - call.serialize(buffer, true) - params.serialize(buffer, true) - return (FunctionDescription(name: "phone.joinGroupCallPresentation", parameters: [("call", String(describing: call)), ("params", String(describing: params))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.phone { - static func leaveGroupCall(call: Api.InputGroupCall, source: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1342404601) - call.serialize(buffer, true) - serializeInt32(source, buffer: buffer, boxed: false) - return (FunctionDescription(name: "phone.leaveGroupCall", parameters: [("call", String(describing: call)), ("source", String(describing: source))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.phone { - static func leaveGroupCallPresentation(call: Api.InputGroupCall) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(475058500) - call.serialize(buffer, true) - return (FunctionDescription(name: "phone.leaveGroupCallPresentation", parameters: [("call", String(describing: call))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.phone { - static func receivedCall(peer: Api.InputPhoneCall) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(399855457) - peer.serialize(buffer, true) - return (FunctionDescription(name: "phone.receivedCall", parameters: [("peer", String(describing: peer))]), 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.phone { - static func requestCall(flags: Int32, userId: Api.InputUser, randomId: Int32, gAHash: Buffer, `protocol`: Api.PhoneCallProtocol) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1124046573) - serializeInt32(flags, buffer: buffer, boxed: false) - userId.serialize(buffer, true) - serializeInt32(randomId, buffer: buffer, boxed: false) - serializeBytes(gAHash, buffer: buffer, boxed: false) - `protocol`.serialize(buffer, true) - return (FunctionDescription(name: "phone.requestCall", parameters: [("flags", String(describing: flags)), ("userId", String(describing: userId)), ("randomId", String(describing: randomId)), ("gAHash", String(describing: gAHash)), ("`protocol`", String(describing: `protocol`))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.PhoneCall? in - let reader = BufferReader(buffer) - var result: Api.phone.PhoneCall? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.phone.PhoneCall - } - return result - }) - } -} -public extension Api.functions.phone { - static func saveCallDebug(peer: Api.InputPhoneCall, debug: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(662363518) - peer.serialize(buffer, true) - debug.serialize(buffer, true) - return (FunctionDescription(name: "phone.saveCallDebug", parameters: [("peer", String(describing: peer)), ("debug", String(describing: debug))]), 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.phone { - static func saveCallLog(peer: Api.InputPhoneCall, file: Api.InputFile) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1092913030) - peer.serialize(buffer, true) - file.serialize(buffer, true) - return (FunctionDescription(name: "phone.saveCallLog", parameters: [("peer", String(describing: peer)), ("file", String(describing: file))]), 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.phone { - static func saveDefaultGroupCallJoinAs(peer: Api.InputPeer, joinAs: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1465786252) - peer.serialize(buffer, true) - joinAs.serialize(buffer, true) - return (FunctionDescription(name: "phone.saveDefaultGroupCallJoinAs", parameters: [("peer", String(describing: peer)), ("joinAs", String(describing: joinAs))]), 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.phone { - static func sendSignalingData(peer: Api.InputPhoneCall, data: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-8744061) - peer.serialize(buffer, true) - serializeBytes(data, buffer: buffer, boxed: false) - return (FunctionDescription(name: "phone.sendSignalingData", parameters: [("peer", String(describing: peer)), ("data", String(describing: data))]), 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.phone { - static func setCallRating(flags: Int32, peer: Api.InputPhoneCall, rating: Int32, comment: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1508562471) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(rating, buffer: buffer, boxed: false) - serializeString(comment, buffer: buffer, boxed: false) - return (FunctionDescription(name: "phone.setCallRating", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("rating", String(describing: rating)), ("comment", String(describing: comment))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.phone { - static func startScheduledGroupCall(call: Api.InputGroupCall) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1451287362) - call.serialize(buffer, true) - return (FunctionDescription(name: "phone.startScheduledGroupCall", parameters: [("call", String(describing: call))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.phone { - static func toggleGroupCallRecord(flags: Int32, call: Api.InputGroupCall, title: String?, videoPortrait: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-248985848) - serializeInt32(flags, buffer: buffer, boxed: false) - call.serialize(buffer, true) - if Int(flags) & Int(1 << 1) != 0 {serializeString(title!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {videoPortrait!.serialize(buffer, true)} - return (FunctionDescription(name: "phone.toggleGroupCallRecord", parameters: [("flags", String(describing: flags)), ("call", String(describing: call)), ("title", String(describing: title)), ("videoPortrait", String(describing: videoPortrait))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.phone { - static func toggleGroupCallSettings(flags: Int32, call: Api.InputGroupCall, joinMuted: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1958458429) - serializeInt32(flags, buffer: buffer, boxed: false) - call.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {joinMuted!.serialize(buffer, true)} - return (FunctionDescription(name: "phone.toggleGroupCallSettings", parameters: [("flags", String(describing: flags)), ("call", String(describing: call)), ("joinMuted", String(describing: joinMuted))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.phone { - static func toggleGroupCallStartSubscription(call: Api.InputGroupCall, subscribed: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(563885286) - call.serialize(buffer, true) - subscribed.serialize(buffer, true) - return (FunctionDescription(name: "phone.toggleGroupCallStartSubscription", parameters: [("call", String(describing: call)), ("subscribed", String(describing: subscribed))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.photos { - static func deletePhotos(id: [Api.InputPhoto]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int64]>) { - let buffer = Buffer() - buffer.appendInt32(-2016444625) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - item.serialize(buffer, true) - } - return (FunctionDescription(name: "photos.deletePhotos", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int64]? in - let reader = BufferReader(buffer) - var result: [Int64]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) - } - return result - }) - } -} -public extension Api.functions.photos { - static func getUserPhotos(userId: Api.InputUser, offset: Int32, maxId: Int64, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1848823128) - userId.serialize(buffer, true) - serializeInt32(offset, buffer: buffer, boxed: false) - serializeInt64(maxId, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "photos.getUserPhotos", parameters: [("userId", String(describing: userId)), ("offset", String(describing: offset)), ("maxId", String(describing: maxId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photos? in - let reader = BufferReader(buffer) - var result: Api.photos.Photos? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.photos.Photos - } - return result - }) - } -} -public extension Api.functions.photos { - static func updateProfilePhoto(flags: Int32, bot: Api.InputUser?, id: Api.InputPhoto) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(166207545) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {bot!.serialize(buffer, true)} - id.serialize(buffer, true) - return (FunctionDescription(name: "photos.updateProfilePhoto", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in - let reader = BufferReader(buffer) - var result: Api.photos.Photo? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.photos.Photo - } - return result - }) - } -} -public extension Api.functions.photos { - static func uploadContactProfilePhoto(flags: Int32, userId: Api.InputUser, file: Api.InputFile?, video: Api.InputFile?, videoStartTs: Double?, videoEmojiMarkup: Api.VideoSize?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-515093903) - serializeInt32(flags, buffer: buffer, boxed: false) - userId.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {file!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {video!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {serializeDouble(videoStartTs!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 5) != 0 {videoEmojiMarkup!.serialize(buffer, true)} - return (FunctionDescription(name: "photos.uploadContactProfilePhoto", parameters: [("flags", String(describing: flags)), ("userId", String(describing: userId)), ("file", String(describing: file)), ("video", String(describing: video)), ("videoStartTs", String(describing: videoStartTs)), ("videoEmojiMarkup", String(describing: videoEmojiMarkup))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in - let reader = BufferReader(buffer) - var result: Api.photos.Photo? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.photos.Photo - } - return result - }) - } -} -public extension Api.functions.photos { - static func uploadProfilePhoto(flags: Int32, bot: Api.InputUser?, file: Api.InputFile?, video: Api.InputFile?, videoStartTs: Double?, videoEmojiMarkup: Api.VideoSize?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(59286453) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 5) != 0 {bot!.serialize(buffer, true)} - if Int(flags) & Int(1 << 0) != 0 {file!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {video!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {serializeDouble(videoStartTs!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {videoEmojiMarkup!.serialize(buffer, true)} - return (FunctionDescription(name: "photos.uploadProfilePhoto", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("file", String(describing: file)), ("video", String(describing: video)), ("videoStartTs", String(describing: videoStartTs)), ("videoEmojiMarkup", String(describing: videoEmojiMarkup))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in - let reader = BufferReader(buffer) - var result: Api.photos.Photo? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.photos.Photo - } - return result - }) - } -} -public extension Api.functions.premium { - static func applyBoost(flags: Int32, slots: [Int32]?, peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1803396934) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(slots!.count)) - for item in slots! { - serializeInt32(item, buffer: buffer, boxed: false) - }} - peer.serialize(buffer, true) - return (FunctionDescription(name: "premium.applyBoost", parameters: [("flags", String(describing: flags)), ("slots", String(describing: slots)), ("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.premium.MyBoosts? in - let reader = BufferReader(buffer) - var result: Api.premium.MyBoosts? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.premium.MyBoosts - } - return result - }) - } -} -public extension Api.functions.premium { - static func getBoostsList(flags: Int32, peer: Api.InputPeer, offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1626764896) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeString(offset, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "premium.getBoostsList", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.premium.BoostsList? in - let reader = BufferReader(buffer) - var result: Api.premium.BoostsList? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.premium.BoostsList - } - return result - }) - } -} -public extension Api.functions.premium { - static func getBoostsStatus(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(70197089) - peer.serialize(buffer, true) - return (FunctionDescription(name: "premium.getBoostsStatus", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.premium.BoostsStatus? in - let reader = BufferReader(buffer) - var result: Api.premium.BoostsStatus? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.premium.BoostsStatus - } - return result - }) - } -} -public extension Api.functions.premium { - static func getMyBoosts() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(199719754) - - return (FunctionDescription(name: "premium.getMyBoosts", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.premium.MyBoosts? in - let reader = BufferReader(buffer) - var result: Api.premium.MyBoosts? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.premium.MyBoosts - } - return result - }) - } -} -public extension Api.functions.stats { - static func getBroadcastStats(flags: Int32, channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1421720550) - serializeInt32(flags, buffer: buffer, boxed: false) - channel.serialize(buffer, true) - return (FunctionDescription(name: "stats.getBroadcastStats", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stats.BroadcastStats? in - let reader = BufferReader(buffer) - var result: Api.stats.BroadcastStats? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.stats.BroadcastStats - } - return result - }) - } -} -public extension Api.functions.stats { - static func getMegagroupStats(flags: Int32, channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-589330937) - serializeInt32(flags, buffer: buffer, boxed: false) - channel.serialize(buffer, true) - return (FunctionDescription(name: "stats.getMegagroupStats", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stats.MegagroupStats? in - let reader = BufferReader(buffer) - var result: Api.stats.MegagroupStats? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.stats.MegagroupStats - } - return result - }) - } -} -public extension Api.functions.stats { - static func getMessagePublicForwards(channel: Api.InputChannel, msgId: Int32, offsetRate: Int32, offsetPeer: Api.InputPeer, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1445996571) - channel.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - serializeInt32(offsetRate, buffer: buffer, boxed: false) - offsetPeer.serialize(buffer, true) - serializeInt32(offsetId, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "stats.getMessagePublicForwards", parameters: [("channel", String(describing: channel)), ("msgId", String(describing: msgId)), ("offsetRate", String(describing: offsetRate)), ("offsetPeer", String(describing: offsetPeer)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in - let reader = BufferReader(buffer) - var result: Api.messages.Messages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Messages - } - return result - }) - } -} -public extension Api.functions.stats { - static func getMessageStats(flags: Int32, channel: Api.InputChannel, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1226791947) - serializeInt32(flags, buffer: buffer, boxed: false) - channel.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "stats.getMessageStats", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stats.MessageStats? in - let reader = BufferReader(buffer) - var result: Api.stats.MessageStats? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.stats.MessageStats - } - return result - }) - } -} -public extension Api.functions.stats { - static func loadAsyncGraph(flags: Int32, token: String, x: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1646092192) - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(token, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt64(x!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "stats.loadAsyncGraph", parameters: [("flags", String(describing: flags)), ("token", String(describing: token)), ("x", String(describing: x))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.StatsGraph? in - let reader = BufferReader(buffer) - var result: Api.StatsGraph? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - return result - }) - } -} -public extension Api.functions.stickers { - static func addStickerToSet(stickerset: Api.InputStickerSet, sticker: Api.InputStickerSetItem) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-2041315650) - stickerset.serialize(buffer, true) - sticker.serialize(buffer, true) - return (FunctionDescription(name: "stickers.addStickerToSet", parameters: [("stickerset", String(describing: stickerset)), ("sticker", String(describing: sticker))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in - let reader = BufferReader(buffer) - var result: Api.messages.StickerSet? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.StickerSet - } - return result - }) - } -} -public extension Api.functions.stickers { - static func changeSticker(flags: Int32, sticker: Api.InputDocument, emoji: String?, maskCoords: Api.MaskCoords?, keywords: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-179077444) - serializeInt32(flags, buffer: buffer, boxed: false) - sticker.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {serializeString(emoji!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {maskCoords!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(keywords!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "stickers.changeSticker", parameters: [("flags", String(describing: flags)), ("sticker", String(describing: sticker)), ("emoji", String(describing: emoji)), ("maskCoords", String(describing: maskCoords)), ("keywords", String(describing: keywords))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in - let reader = BufferReader(buffer) - var result: Api.messages.StickerSet? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.StickerSet - } - return result - }) - } -} -public extension Api.functions.stickers { - static func changeStickerPosition(sticker: Api.InputDocument, position: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-4795190) - sticker.serialize(buffer, true) - serializeInt32(position, buffer: buffer, boxed: false) - return (FunctionDescription(name: "stickers.changeStickerPosition", parameters: [("sticker", String(describing: sticker)), ("position", String(describing: position))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in - let reader = BufferReader(buffer) - var result: Api.messages.StickerSet? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.StickerSet - } - return result - }) - } -} -public extension Api.functions.stickers { - static func checkShortName(shortName: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(676017721) - serializeString(shortName, buffer: buffer, boxed: false) - return (FunctionDescription(name: "stickers.checkShortName", parameters: [("shortName", String(describing: shortName))]), 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.stickers { - static func createStickerSet(flags: Int32, userId: Api.InputUser, title: String, shortName: String, thumb: Api.InputDocument?, stickers: [Api.InputStickerSetItem], software: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1876841625) - serializeInt32(flags, buffer: buffer, boxed: false) - userId.serialize(buffer, true) - serializeString(title, buffer: buffer, boxed: false) - serializeString(shortName, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {thumb!.serialize(buffer, true)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(stickers.count)) - for item in stickers { - item.serialize(buffer, true) + state.serialize(buffer, true) + break + case .differenceEmpty(let date, let seq): + if boxed { + buffer.appendInt32(1567990072) } - if Int(flags) & Int(1 << 3) != 0 {serializeString(software!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "stickers.createStickerSet", parameters: [("flags", String(describing: flags)), ("userId", String(describing: userId)), ("title", String(describing: title)), ("shortName", String(describing: shortName)), ("thumb", String(describing: thumb)), ("stickers", String(describing: stickers)), ("software", String(describing: software))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in - let reader = BufferReader(buffer) - var result: Api.messages.StickerSet? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.StickerSet - } - return result - }) - } -} -public extension Api.functions.stickers { - static func deleteStickerSet(stickerset: Api.InputStickerSet) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-2022685804) - stickerset.serialize(buffer, true) - return (FunctionDescription(name: "stickers.deleteStickerSet", parameters: [("stickerset", String(describing: stickerset))]), 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.stickers { - static func removeStickerFromSet(sticker: Api.InputDocument) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-143257775) - sticker.serialize(buffer, true) - return (FunctionDescription(name: "stickers.removeStickerFromSet", parameters: [("sticker", String(describing: sticker))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in - let reader = BufferReader(buffer) - var result: Api.messages.StickerSet? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.StickerSet - } - return result - }) - } -} -public extension Api.functions.stickers { - static func renameStickerSet(stickerset: Api.InputStickerSet, title: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(306912256) - stickerset.serialize(buffer, true) - serializeString(title, buffer: buffer, boxed: false) - return (FunctionDescription(name: "stickers.renameStickerSet", parameters: [("stickerset", String(describing: stickerset)), ("title", String(describing: title))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in - let reader = BufferReader(buffer) - var result: Api.messages.StickerSet? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.StickerSet - } - return result - }) - } -} -public extension Api.functions.stickers { - static func setStickerSetThumb(flags: Int32, stickerset: Api.InputStickerSet, thumb: Api.InputDocument?, thumbDocumentId: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1486204014) - serializeInt32(flags, buffer: buffer, boxed: false) - stickerset.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {thumb!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {serializeInt64(thumbDocumentId!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "stickers.setStickerSetThumb", parameters: [("flags", String(describing: flags)), ("stickerset", String(describing: stickerset)), ("thumb", String(describing: thumb)), ("thumbDocumentId", String(describing: thumbDocumentId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in - let reader = BufferReader(buffer) - var result: Api.messages.StickerSet? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.StickerSet - } - return result - }) - } -} -public extension Api.functions.stickers { - static func suggestShortName(title: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1303364867) - serializeString(title, buffer: buffer, boxed: false) - return (FunctionDescription(name: "stickers.suggestShortName", parameters: [("title", String(describing: title))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stickers.SuggestedShortName? in - let reader = BufferReader(buffer) - var result: Api.stickers.SuggestedShortName? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.stickers.SuggestedShortName - } - return result - }) - } -} -public extension Api.functions.stories { - static func activateStealthMode(flags: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1471926630) - serializeInt32(flags, buffer: buffer, boxed: false) - return (FunctionDescription(name: "stories.activateStealthMode", parameters: [("flags", String(describing: flags))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.stories { - static func canSendStory(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-941629475) - peer.serialize(buffer, true) - return (FunctionDescription(name: "stories.canSendStory", parameters: [("peer", String(describing: peer))]), 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.stories { - static func deleteStories(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int32]>) { - let buffer = Buffer() - buffer.appendInt32(-1369842849) - peer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) + 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) } - return (FunctionDescription(name: "stories.deleteStories", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int32]? in - let reader = BufferReader(buffer) - var result: [Int32]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - return result - }) - } -} -public extension Api.functions.stories { - static func editStory(flags: Int32, peer: Api.InputPeer, id: Int32, media: Api.InputMedia?, mediaAreas: [Api.MediaArea]?, caption: String?, entities: [Api.MessageEntity]?, privacyRules: [Api.InputPrivacyRule]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1249658298) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {media!.serialize(buffer, true)} - if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(mediaAreas!.count)) - for item in mediaAreas! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 1) != 0 {serializeString(caption!, 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 << 2) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(privacyRules!.count)) - for item in privacyRules! { - item.serialize(buffer, true) - }} - return (FunctionDescription(name: "stories.editStory", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id)), ("media", String(describing: media)), ("mediaAreas", String(describing: mediaAreas)), ("caption", String(describing: caption)), ("entities", String(describing: entities)), ("privacyRules", String(describing: privacyRules))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.stories { - static func exportStoryLink(peer: Api.InputPeer, id: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2072899360) - peer.serialize(buffer, true) - serializeInt32(id, buffer: buffer, boxed: false) - return (FunctionDescription(name: "stories.exportStoryLink", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedStoryLink? in - let reader = BufferReader(buffer) - var result: Api.ExportedStoryLink? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.ExportedStoryLink - } - return result - }) - } -} -public extension Api.functions.stories { - static func getAllReadPeerStories() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1688541191) - - return (FunctionDescription(name: "stories.getAllReadPeerStories", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.stories { - static func getAllStories(flags: Int32, state: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-290400731) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(state!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "stories.getAllStories", parameters: [("flags", String(describing: flags)), ("state", String(describing: state))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.AllStories? in - let reader = BufferReader(buffer) - var result: Api.stories.AllStories? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.stories.AllStories - } - return result - }) - } -} -public extension Api.functions.stories { - static func getChatsToSend() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1519744160) - - return (FunctionDescription(name: "stories.getChatsToSend", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Chats? in - let reader = BufferReader(buffer) - var result: Api.messages.Chats? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Chats - } - return result - }) - } -} -public extension Api.functions.stories { - static func getPeerMaxIDs(id: [Api.InputPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int32]>) { - let buffer = Buffer() - buffer.appendInt32(1398375363) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { + buffer.appendInt32(Int32(newMessages.count)) + for item in newMessages { item.serialize(buffer, true) } - return (FunctionDescription(name: "stories.getPeerMaxIDs", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int32]? in - let reader = BufferReader(buffer) - var result: [Int32]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - return result - }) - } -} -public extension Api.functions.stories { - static func getPeerStories(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(743103056) - peer.serialize(buffer, true) - return (FunctionDescription(name: "stories.getPeerStories", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.PeerStories? in - let reader = BufferReader(buffer) - var result: Api.stories.PeerStories? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.stories.PeerStories - } - return result - }) - } -} -public extension Api.functions.stories { - static func getPinnedStories(peer: Api.InputPeer, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1478600156) - peer.serialize(buffer, true) - serializeInt32(offsetId, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "stories.getPinnedStories", parameters: [("peer", String(describing: peer)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.Stories? in - let reader = BufferReader(buffer) - var result: Api.stories.Stories? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.stories.Stories - } - return result - }) - } -} -public extension Api.functions.stories { - static func getStoriesArchive(peer: Api.InputPeer, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1271586794) - peer.serialize(buffer, true) - serializeInt32(offsetId, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "stories.getStoriesArchive", parameters: [("peer", String(describing: peer)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.Stories? in - let reader = BufferReader(buffer) - var result: Api.stories.Stories? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.stories.Stories - } - return result - }) - } -} -public extension Api.functions.stories { - static func getStoriesByID(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1467271796) - peer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "stories.getStoriesByID", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.Stories? in - let reader = BufferReader(buffer) - var result: Api.stories.Stories? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.stories.Stories - } - return result - }) - } -} -public extension Api.functions.stories { - static func getStoriesViews(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(685862088) - peer.serialize(buffer, true) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) + buffer.appendInt32(Int32(newEncryptedMessages.count)) + for item in newEncryptedMessages { + item.serialize(buffer, true) } - return (FunctionDescription(name: "stories.getStoriesViews", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.StoryViews? in - let reader = BufferReader(buffer) - var result: Api.stories.StoryViews? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.stories.StoryViews - } - return result - }) - } -} -public extension Api.functions.stories { - static func getStoryViewsList(flags: Int32, peer: Api.InputPeer, q: String?, id: Int32, offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2127707223) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 1) != 0 {serializeString(q!, buffer: buffer, boxed: false)} - serializeInt32(id, buffer: buffer, boxed: false) - serializeString(offset, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "stories.getStoryViewsList", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("q", String(describing: q)), ("id", String(describing: id)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.StoryViewsList? in - let reader = BufferReader(buffer) - var result: Api.stories.StoryViewsList? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.stories.StoryViewsList - } - return result - }) - } -} -public extension Api.functions.stories { - static func incrementStoryViews(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1308456197) - peer.serialize(buffer, true) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) + buffer.appendInt32(Int32(otherUpdates.count)) + for item in otherUpdates { + item.serialize(buffer, true) } - return (FunctionDescription(name: "stories.incrementStoryViews", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), 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.stories { - static func readStories(peer: Api.InputPeer, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int32]>) { - let buffer = Buffer() - buffer.appendInt32(-1521034552) - peer.serialize(buffer, true) - serializeInt32(maxId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "stories.readStories", parameters: [("peer", String(describing: peer)), ("maxId", String(describing: maxId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int32]? in - let reader = BufferReader(buffer) - var result: [Int32]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - return result - }) - } -} -public extension Api.functions.stories { - static func report(peer: Api.InputPeer, id: [Int32], reason: Api.ReportReason, message: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(421788300) - peer.serialize(buffer, true) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) - } - reason.serialize(buffer, true) - serializeString(message, buffer: buffer, boxed: false) - return (FunctionDescription(name: "stories.report", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("reason", String(describing: reason)), ("message", String(describing: message))]), 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.stories { - static func sendReaction(flags: Int32, peer: Api.InputPeer, storyId: Int32, reaction: Api.Reaction) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2144810674) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(storyId, buffer: buffer, boxed: false) - reaction.serialize(buffer, true) - return (FunctionDescription(name: "stories.sendReaction", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("storyId", String(describing: storyId)), ("reaction", String(describing: reaction))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.stories { - static func sendStory(flags: Int32, peer: Api.InputPeer, media: Api.InputMedia, mediaAreas: [Api.MediaArea]?, caption: String?, entities: [Api.MessageEntity]?, privacyRules: [Api.InputPrivacyRule], randomId: Int64, period: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1128843708) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - media.serialize(buffer, true) - if Int(flags) & Int(1 << 5) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(mediaAreas!.count)) - for item in mediaAreas! { + buffer.appendInt32(Int32(chats.count)) + for item in chats { item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 0) != 0 {serializeString(caption!, 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) - }} + } buffer.appendInt32(481674261) - buffer.appendInt32(Int32(privacyRules.count)) - for item in privacyRules { + buffer.appendInt32(Int32(users.count)) + for item in users { item.serialize(buffer, true) } - serializeInt64(randomId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 3) != 0 {serializeInt32(period!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "stories.sendStory", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("media", String(describing: media)), ("mediaAreas", String(describing: mediaAreas)), ("caption", String(describing: caption)), ("entities", String(describing: entities)), ("privacyRules", String(describing: privacyRules)), ("randomId", String(describing: randomId)), ("period", String(describing: period))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} -public extension Api.functions.stories { - static func toggleAllStoriesHidden(hidden: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2082822084) - hidden.serialize(buffer, true) - return (FunctionDescription(name: "stories.toggleAllStoriesHidden", parameters: [("hidden", String(describing: hidden))]), 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.stories { - static func togglePeerStoriesHidden(peer: Api.InputPeer, hidden: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1123805756) - peer.serialize(buffer, true) - hidden.serialize(buffer, true) - return (FunctionDescription(name: "stories.togglePeerStoriesHidden", parameters: [("peer", String(describing: peer)), ("hidden", String(describing: hidden))]), 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.stories { - static func togglePinned(peer: Api.InputPeer, id: [Int32], pinned: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int32]>) { - let buffer = Buffer() - buffer.appendInt32(-1703566865) - peer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) + intermediateState.serialize(buffer, true) + break + case .differenceTooLong(let pts): + if boxed { + buffer.appendInt32(1258196845) } - pinned.serialize(buffer, true) - return (FunctionDescription(name: "stories.togglePinned", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("pinned", String(describing: pinned))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int32]? in - let reader = BufferReader(buffer) - var result: [Int32]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - return result - }) - } -} -public extension Api.functions.updates { - static func getChannelDifference(flags: Int32, channel: Api.InputChannel, filter: Api.ChannelMessagesFilter, pts: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(51854712) - serializeInt32(flags, buffer: buffer, boxed: false) - channel.serialize(buffer, true) - filter.serialize(buffer, true) serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "updates.getChannelDifference", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("filter", String(describing: filter)), ("pts", String(describing: pts)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.updates.ChannelDifference? in - let reader = BufferReader(buffer) - var result: Api.updates.ChannelDifference? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.updates.ChannelDifference - } - return result - }) - } -} -public extension Api.functions.updates { - static func getDifference(flags: Int32, pts: Int32, ptsLimit: Int32?, ptsTotalLimit: Int32?, date: Int32, qts: Int32, qtsLimit: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(432207715) - serializeInt32(flags, 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) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .state(let pts, let qts, let date, let seq, let unreadCount): + if boxed { + buffer.appendInt32(-1519637954) + } serializeInt32(pts, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(ptsLimit!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(ptsTotalLimit!, buffer: buffer, boxed: false)} - serializeInt32(date, buffer: buffer, boxed: false) serializeInt32(qts, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {serializeInt32(qtsLimit!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "updates.getDifference", parameters: [("flags", String(describing: flags)), ("pts", String(describing: pts)), ("ptsLimit", String(describing: ptsLimit)), ("ptsTotalLimit", String(describing: ptsTotalLimit)), ("date", String(describing: date)), ("qts", String(describing: qts)), ("qtsLimit", String(describing: qtsLimit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.updates.Difference? in - let reader = BufferReader(buffer) - var result: Api.updates.Difference? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.updates.Difference - } - return result - }) - } -} -public extension Api.functions.updates { - static func getState() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-304838614) - - return (FunctionDescription(name: "updates.getState", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.updates.State? in - let reader = BufferReader(buffer) - var result: Api.updates.State? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.updates.State - } - return result - }) - } -} -public extension Api.functions.upload { - static func getCdnFile(fileToken: Buffer, offset: Int64, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(962554330) - serializeBytes(fileToken, buffer: buffer, boxed: false) - serializeInt64(offset, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "upload.getCdnFile", parameters: [("fileToken", String(describing: fileToken)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.upload.CdnFile? in - let reader = BufferReader(buffer) - var result: Api.upload.CdnFile? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.upload.CdnFile - } - return result - }) - } -} -public extension Api.functions.upload { - static func getCdnFileHashes(fileToken: Buffer, offset: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.FileHash]>) { - let buffer = Buffer() - buffer.appendInt32(-1847836879) - serializeBytes(fileToken, buffer: buffer, boxed: false) - serializeInt64(offset, buffer: buffer, boxed: false) - return (FunctionDescription(name: "upload.getCdnFileHashes", parameters: [("fileToken", String(describing: fileToken)), ("offset", String(describing: offset))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.FileHash]? in - let reader = BufferReader(buffer) - var result: [Api.FileHash]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.FileHash.self) - } - return result - }) - } -} -public extension Api.functions.upload { - static func getFile(flags: Int32, location: Api.InputFileLocation, offset: Int64, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1101843010) - serializeInt32(flags, buffer: buffer, boxed: false) - location.serialize(buffer, true) - serializeInt64(offset, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "upload.getFile", parameters: [("flags", String(describing: flags)), ("location", String(describing: location)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.upload.File? in - let reader = BufferReader(buffer) - var result: Api.upload.File? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.upload.File - } - return result - }) - } -} -public extension Api.functions.upload { - static func getFileHashes(location: Api.InputFileLocation, offset: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.FileHash]>) { - let buffer = Buffer() - buffer.appendInt32(-1856595926) - location.serialize(buffer, true) - serializeInt64(offset, buffer: buffer, boxed: false) - return (FunctionDescription(name: "upload.getFileHashes", parameters: [("location", String(describing: location)), ("offset", String(describing: offset))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.FileHash]? in - let reader = BufferReader(buffer) - var result: [Api.FileHash]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.FileHash.self) - } - return result - }) - } -} -public extension Api.functions.upload { - static func getWebFile(location: Api.InputWebFileLocation, offset: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(619086221) - location.serialize(buffer, true) - serializeInt32(offset, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "upload.getWebFile", parameters: [("location", String(describing: location)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.upload.WebFile? in - let reader = BufferReader(buffer) - var result: Api.upload.WebFile? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.upload.WebFile - } - return result - }) - } -} -public extension Api.functions.upload { - static func reuploadCdnFile(fileToken: Buffer, requestToken: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.FileHash]>) { - let buffer = Buffer() - buffer.appendInt32(-1691921240) - serializeBytes(fileToken, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt32(seq, buffer: buffer, boxed: false) + serializeInt32(unreadCount, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .state(let pts, let qts, let date, let seq, let unreadCount): + return ("state", [("pts", pts as Any), ("qts", qts as Any), ("date", date as Any), ("seq", seq as Any), ("unreadCount", unreadCount as Any)]) + } + } + + public static func parse_state(_ reader: BufferReader) -> State? { + 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() + 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.updates.State.state(pts: _1!, qts: _2!, date: _3!, seq: _4!, unreadCount: _5!) + } + else { + return nil + } + } + + } +} +public extension Api.upload { + enum CdnFile: TypeConstructorDescription { + case cdnFile(bytes: Buffer) + case cdnFileReuploadNeeded(requestToken: Buffer) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .cdnFile(let bytes): + if boxed { + buffer.appendInt32(-1449145777) + } + serializeBytes(bytes, buffer: buffer, boxed: false) + break + case .cdnFileReuploadNeeded(let requestToken): + if boxed { + buffer.appendInt32(-290921362) + } serializeBytes(requestToken, buffer: buffer, boxed: false) - return (FunctionDescription(name: "upload.reuploadCdnFile", parameters: [("fileToken", String(describing: fileToken)), ("requestToken", String(describing: requestToken))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.FileHash]? in - let reader = BufferReader(buffer) - var result: [Api.FileHash]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.FileHash.self) - } - return result - }) - } -} -public extension Api.functions.upload { - static func saveBigFilePart(fileId: Int64, filePart: Int32, fileTotalParts: Int32, bytes: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-562337987) - serializeInt64(fileId, buffer: buffer, boxed: false) - serializeInt32(filePart, buffer: buffer, boxed: false) - serializeInt32(fileTotalParts, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .cdnFile(let bytes): + return ("cdnFile", [("bytes", bytes as Any)]) + case .cdnFileReuploadNeeded(let requestToken): + return ("cdnFileReuploadNeeded", [("requestToken", requestToken as Any)]) + } + } + + public static func parse_cdnFile(_ reader: BufferReader) -> CdnFile? { + var _1: Buffer? + _1 = parseBytes(reader) + let _c1 = _1 != nil + if _c1 { + return Api.upload.CdnFile.cdnFile(bytes: _1!) + } + else { + return nil + } + } + public static func parse_cdnFileReuploadNeeded(_ reader: BufferReader) -> CdnFile? { + var _1: Buffer? + _1 = parseBytes(reader) + let _c1 = _1 != nil + if _c1 { + return Api.upload.CdnFile.cdnFileReuploadNeeded(requestToken: _1!) + } + else { + return nil + } + } + + } +} +public extension Api.upload { + enum File: TypeConstructorDescription { + case file(type: Api.storage.FileType, mtime: Int32, bytes: Buffer) + case fileCdnRedirect(dcId: Int32, fileToken: Buffer, encryptionKey: Buffer, encryptionIv: Buffer, fileHashes: [Api.FileHash]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .file(let type, let mtime, let bytes): + if boxed { + buffer.appendInt32(157948117) + } + type.serialize(buffer, true) + serializeInt32(mtime, buffer: buffer, boxed: false) serializeBytes(bytes, buffer: buffer, boxed: false) - return (FunctionDescription(name: "upload.saveBigFilePart", parameters: [("fileId", String(describing: fileId)), ("filePart", String(describing: filePart)), ("fileTotalParts", String(describing: fileTotalParts)), ("bytes", String(describing: bytes))]), 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.upload { - static func saveFilePart(fileId: Int64, filePart: Int32, bytes: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1291540959) - serializeInt64(fileId, buffer: buffer, boxed: false) - serializeInt32(filePart, buffer: buffer, boxed: false) + break + case .fileCdnRedirect(let dcId, let fileToken, let encryptionKey, let encryptionIv, let fileHashes): + if boxed { + buffer.appendInt32(-242427324) + } + serializeInt32(dcId, buffer: buffer, boxed: false) + serializeBytes(fileToken, buffer: buffer, boxed: false) + serializeBytes(encryptionKey, buffer: buffer, boxed: false) + serializeBytes(encryptionIv, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(fileHashes.count)) + for item in fileHashes { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .file(let type, let mtime, let bytes): + return ("file", [("type", type as Any), ("mtime", mtime as Any), ("bytes", bytes as Any)]) + case .fileCdnRedirect(let dcId, let fileToken, let encryptionKey, let encryptionIv, let fileHashes): + return ("fileCdnRedirect", [("dcId", dcId as Any), ("fileToken", fileToken as Any), ("encryptionKey", encryptionKey as Any), ("encryptionIv", encryptionIv as Any), ("fileHashes", fileHashes as Any)]) + } + } + + public static func parse_file(_ reader: BufferReader) -> File? { + var _1: Api.storage.FileType? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.storage.FileType + } + var _2: Int32? + _2 = reader.readInt32() + var _3: Buffer? + _3 = parseBytes(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.upload.File.file(type: _1!, mtime: _2!, bytes: _3!) + } + else { + return nil + } + } + public static func parse_fileCdnRedirect(_ reader: BufferReader) -> File? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Buffer? + _2 = parseBytes(reader) + var _3: Buffer? + _3 = parseBytes(reader) + var _4: Buffer? + _4 = parseBytes(reader) + var _5: [Api.FileHash]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.FileHash.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.upload.File.fileCdnRedirect(dcId: _1!, fileToken: _2!, encryptionKey: _3!, encryptionIv: _4!, fileHashes: _5!) + } + else { + return nil + } + } + + } +} +public extension Api.upload { + enum WebFile: TypeConstructorDescription { + case webFile(size: Int32, mimeType: String, fileType: Api.storage.FileType, mtime: Int32, bytes: Buffer) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .webFile(let size, let mimeType, let fileType, let mtime, let bytes): + if boxed { + buffer.appendInt32(568808380) + } + serializeInt32(size, buffer: buffer, boxed: false) + serializeString(mimeType, buffer: buffer, boxed: false) + fileType.serialize(buffer, true) + serializeInt32(mtime, buffer: buffer, boxed: false) serializeBytes(bytes, buffer: buffer, boxed: false) - return (FunctionDescription(name: "upload.saveFilePart", parameters: [("fileId", String(describing: fileId)), ("filePart", String(describing: filePart)), ("bytes", String(describing: bytes))]), 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.users { - static func getFullUser(id: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1240508136) - id.serialize(buffer, true) - return (FunctionDescription(name: "users.getFullUser", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.users.UserFull? in - let reader = BufferReader(buffer) - var result: Api.users.UserFull? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.users.UserFull - } - return result - }) - } -} -public extension Api.functions.users { - static func getUsers(id: [Api.InputUser]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.User]>) { - let buffer = Buffer() - buffer.appendInt32(227648840) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .webFile(let size, let mimeType, let fileType, let mtime, let bytes): + return ("webFile", [("size", size as Any), ("mimeType", mimeType as Any), ("fileType", fileType as Any), ("mtime", mtime as Any), ("bytes", bytes as Any)]) + } + } + + public static func parse_webFile(_ reader: BufferReader) -> WebFile? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + var _3: Api.storage.FileType? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.storage.FileType + } + var _4: Int32? + _4 = reader.readInt32() + var _5: Buffer? + _5 = parseBytes(reader) + 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.upload.WebFile.webFile(size: _1!, mimeType: _2!, fileType: _3!, mtime: _4!, bytes: _5!) + } + else { + return nil + } + } + + } +} +public extension Api.users { + enum UserFull: TypeConstructorDescription { + case userFull(fullUser: Api.UserFull, chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .userFull(let fullUser, let chats, let users): + if boxed { + buffer.appendInt32(997004590) + } + fullUser.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { item.serialize(buffer, true) } - return (FunctionDescription(name: "users.getUsers", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.User]? in - let reader = BufferReader(buffer) - var result: [Api.User]? - if let _ = reader.readInt32() { - result = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - return result - }) - } -} -public extension Api.functions.users { - static func setSecureValueErrors(id: Api.InputUser, errors: [Api.SecureValueError]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1865902923) - id.serialize(buffer, true) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(errors.count)) - for item in errors { + buffer.appendInt32(Int32(users.count)) + for item in users { item.serialize(buffer, true) } - return (FunctionDescription(name: "users.setSecureValueErrors", parameters: [("id", String(describing: id)), ("errors", String(describing: errors))]), 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 - }) - } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .userFull(let fullUser, let chats, let users): + return ("userFull", [("fullUser", fullUser as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_userFull(_ reader: BufferReader) -> UserFull? { + var _1: Api.UserFull? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.UserFull + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.users.UserFull.userFull(fullUser: _1!, chats: _2!, users: _3!) + } + else { + return nil + } + } + + } } diff --git a/submodules/TelegramApi/Sources/Api32.swift b/submodules/TelegramApi/Sources/Api32.swift new file mode 100644 index 00000000000..fd921095daf --- /dev/null +++ b/submodules/TelegramApi/Sources/Api32.swift @@ -0,0 +1,9515 @@ +public extension Api.functions.account { + static func acceptAuthorization(botId: Int64, scope: String, publicKey: String, valueHashes: [Api.SecureValueHash], credentials: Api.SecureCredentialsEncrypted) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-202552205) + serializeInt64(botId, buffer: buffer, boxed: false) + serializeString(scope, buffer: buffer, boxed: false) + serializeString(publicKey, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(valueHashes.count)) + for item in valueHashes { + item.serialize(buffer, true) + } + credentials.serialize(buffer, true) + return (FunctionDescription(name: "account.acceptAuthorization", parameters: [("botId", String(describing: botId)), ("scope", String(describing: scope)), ("publicKey", String(describing: publicKey)), ("valueHashes", String(describing: valueHashes)), ("credentials", String(describing: credentials))]), 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 cancelPasswordEmail() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1043606090) + + return (FunctionDescription(name: "account.cancelPasswordEmail", parameters: []), 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 changeAuthorizationSettings(flags: Int32, hash: Int64, encryptedRequestsDisabled: Api.Bool?, callRequestsDisabled: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1089766498) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {encryptedRequestsDisabled!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {callRequestsDisabled!.serialize(buffer, true)} + return (FunctionDescription(name: "account.changeAuthorizationSettings", parameters: [("flags", String(describing: flags)), ("hash", String(describing: hash)), ("encryptedRequestsDisabled", String(describing: encryptedRequestsDisabled)), ("callRequestsDisabled", String(describing: callRequestsDisabled))]), 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 changePhone(phoneNumber: String, phoneCodeHash: String, phoneCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1891839707) + serializeString(phoneNumber, buffer: buffer, boxed: false) + serializeString(phoneCodeHash, buffer: buffer, boxed: false) + serializeString(phoneCode, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.changePhone", parameters: [("phoneNumber", String(describing: phoneNumber)), ("phoneCodeHash", String(describing: phoneCodeHash)), ("phoneCode", String(describing: phoneCode))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.User? in + let reader = BufferReader(buffer) + var result: Api.User? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.User + } + return result + }) + } +} +public extension Api.functions.account { + static func checkUsername(username: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(655677548) + serializeString(username, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.checkUsername", parameters: [("username", String(describing: username))]), 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 clearRecentEmojiStatuses() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(404757166) + + return (FunctionDescription(name: "account.clearRecentEmojiStatuses", parameters: []), 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 confirmPasswordEmail(code: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1881204448) + serializeString(code, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.confirmPasswordEmail", parameters: [("code", String(describing: code))]), 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 confirmPhone(phoneCodeHash: String, phoneCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1596029123) + serializeString(phoneCodeHash, buffer: buffer, boxed: false) + serializeString(phoneCode, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.confirmPhone", parameters: [("phoneCodeHash", String(describing: phoneCodeHash)), ("phoneCode", String(describing: phoneCode))]), 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 createTheme(flags: Int32, slug: String, title: String, document: Api.InputDocument?, settings: [Api.InputThemeSettings]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1697530880) + serializeInt32(flags, 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) + }} + return (FunctionDescription(name: "account.createTheme", parameters: [("flags", String(describing: flags)), ("slug", String(describing: slug)), ("title", String(describing: title)), ("document", String(describing: document)), ("settings", String(describing: settings))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Theme? in + let reader = BufferReader(buffer) + var result: Api.Theme? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Theme + } + return result + }) + } +} +public extension Api.functions.account { + static func declinePasswordReset() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1284770294) + + return (FunctionDescription(name: "account.declinePasswordReset", parameters: []), 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 deleteAccount(flags: Int32, reason: String, password: Api.InputCheckPasswordSRP?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1564422284) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(reason, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {password!.serialize(buffer, true)} + return (FunctionDescription(name: "account.deleteAccount", parameters: [("flags", String(describing: flags)), ("reason", String(describing: reason)), ("password", String(describing: password))]), 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 deleteAutoSaveExceptions() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1404829728) + + return (FunctionDescription(name: "account.deleteAutoSaveExceptions", parameters: []), 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 deleteSecureValue(types: [Api.SecureValueType]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1199522741) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(types.count)) + for item in types { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "account.deleteSecureValue", parameters: [("types", String(describing: types))]), 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 finishTakeoutSession(flags: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(489050862) + serializeInt32(flags, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.finishTakeoutSession", parameters: [("flags", String(describing: flags))]), 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 getAccountTTL() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(150761757) + + return (FunctionDescription(name: "account.getAccountTTL", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.AccountDaysTTL? in + let reader = BufferReader(buffer) + var result: Api.AccountDaysTTL? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.AccountDaysTTL + } + return result + }) + } +} +public extension Api.functions.account { + static func getAllSecureValues() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.SecureValue]>) { + let buffer = Buffer() + buffer.appendInt32(-1299661699) + + return (FunctionDescription(name: "account.getAllSecureValues", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.SecureValue]? in + let reader = BufferReader(buffer) + var result: [Api.SecureValue]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureValue.self) + } + return result + }) + } +} +public extension Api.functions.account { + static func getAuthorizationForm(botId: Int64, scope: String, publicKey: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1456907910) + serializeInt64(botId, buffer: buffer, boxed: false) + serializeString(scope, buffer: buffer, boxed: false) + serializeString(publicKey, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getAuthorizationForm", parameters: [("botId", String(describing: botId)), ("scope", String(describing: scope)), ("publicKey", String(describing: publicKey))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.AuthorizationForm? in + let reader = BufferReader(buffer) + var result: Api.account.AuthorizationForm? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.AuthorizationForm + } + return result + }) + } +} +public extension Api.functions.account { + static func getAuthorizations() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-484392616) + + return (FunctionDescription(name: "account.getAuthorizations", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.Authorizations? in + let reader = BufferReader(buffer) + var result: Api.account.Authorizations? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.Authorizations + } + return result + }) + } +} +public extension Api.functions.account { + static func getAutoDownloadSettings() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1457130303) + + return (FunctionDescription(name: "account.getAutoDownloadSettings", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.AutoDownloadSettings? in + let reader = BufferReader(buffer) + var result: Api.account.AutoDownloadSettings? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.AutoDownloadSettings + } + return result + }) + } +} +public extension Api.functions.account { + static func getAutoSaveSettings() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1379156774) + + return (FunctionDescription(name: "account.getAutoSaveSettings", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.AutoSaveSettings? in + let reader = BufferReader(buffer) + var result: Api.account.AutoSaveSettings? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.AutoSaveSettings + } + return result + }) + } +} +public extension Api.functions.account { + static func getChatThemes(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-700916087) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getChatThemes", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.Themes? in + let reader = BufferReader(buffer) + var result: Api.account.Themes? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.Themes + } + return result + }) + } +} +public extension Api.functions.account { + static func getContactSignUpNotification() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1626880216) + + return (FunctionDescription(name: "account.getContactSignUpNotification", parameters: []), 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 getContentSettings() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1952756306) + + return (FunctionDescription(name: "account.getContentSettings", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.ContentSettings? in + let reader = BufferReader(buffer) + var result: Api.account.ContentSettings? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.ContentSettings + } + return result + }) + } +} +public extension Api.functions.account { + static func getDefaultBackgroundEmojis(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1509246514) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getDefaultBackgroundEmojis", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EmojiList? in + let reader = BufferReader(buffer) + var result: Api.EmojiList? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.EmojiList + } + return result + }) + } +} +public extension Api.functions.account { + static func getDefaultEmojiStatuses(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-696962170) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getDefaultEmojiStatuses", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.EmojiStatuses? in + let reader = BufferReader(buffer) + var result: Api.account.EmojiStatuses? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.EmojiStatuses + } + return result + }) + } +} +public extension Api.functions.account { + static func getDefaultGroupPhotoEmojis(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1856479058) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getDefaultGroupPhotoEmojis", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EmojiList? in + let reader = BufferReader(buffer) + var result: Api.EmojiList? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.EmojiList + } + return result + }) + } +} +public extension Api.functions.account { + static func getDefaultProfilePhotoEmojis(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-495647960) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getDefaultProfilePhotoEmojis", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EmojiList? in + let reader = BufferReader(buffer) + var result: Api.EmojiList? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.EmojiList + } + return result + }) + } +} +public extension Api.functions.account { + static func getGlobalPrivacySettings() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-349483786) + + return (FunctionDescription(name: "account.getGlobalPrivacySettings", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.GlobalPrivacySettings? in + let reader = BufferReader(buffer) + var result: Api.GlobalPrivacySettings? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.GlobalPrivacySettings + } + return result + }) + } +} +public extension Api.functions.account { + static func getMultiWallPapers(wallpapers: [Api.InputWallPaper]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.WallPaper]>) { + let buffer = Buffer() + buffer.appendInt32(1705865692) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(wallpapers.count)) + for item in wallpapers { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "account.getMultiWallPapers", parameters: [("wallpapers", String(describing: wallpapers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.WallPaper]? in + let reader = BufferReader(buffer) + var result: [Api.WallPaper]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.WallPaper.self) + } + return result + }) + } +} +public extension Api.functions.account { + static func getNotifyExceptions(flags: Int32, peer: Api.InputNotifyPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1398240377) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {peer!.serialize(buffer, true)} + return (FunctionDescription(name: "account.getNotifyExceptions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.account { + static func getNotifySettings(peer: Api.InputNotifyPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(313765169) + peer.serialize(buffer, true) + return (FunctionDescription(name: "account.getNotifySettings", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.PeerNotifySettings? in + let reader = BufferReader(buffer) + var result: Api.PeerNotifySettings? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings + } + return result + }) + } +} +public extension Api.functions.account { + static func getPassword() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1418342645) + + return (FunctionDescription(name: "account.getPassword", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.Password? in + let reader = BufferReader(buffer) + var result: Api.account.Password? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.Password + } + return result + }) + } +} +public extension Api.functions.account { + static func getPasswordSettings(password: Api.InputCheckPasswordSRP) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1663767815) + password.serialize(buffer, true) + return (FunctionDescription(name: "account.getPasswordSettings", parameters: [("password", String(describing: password))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.PasswordSettings? in + let reader = BufferReader(buffer) + var result: Api.account.PasswordSettings? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.PasswordSettings + } + return result + }) + } +} +public extension Api.functions.account { + static func getPrivacy(key: Api.InputPrivacyKey) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-623130288) + key.serialize(buffer, true) + return (FunctionDescription(name: "account.getPrivacy", parameters: [("key", String(describing: key))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.PrivacyRules? in + let reader = BufferReader(buffer) + var result: Api.account.PrivacyRules? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.PrivacyRules + } + return result + }) + } +} +public extension Api.functions.account { + static func getRecentEmojiStatuses(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(257392901) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getRecentEmojiStatuses", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.EmojiStatuses? in + let reader = BufferReader(buffer) + var result: Api.account.EmojiStatuses? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.EmojiStatuses + } + return result + }) + } +} +public extension Api.functions.account { + static func getSavedRingtones(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-510647672) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getSavedRingtones", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.SavedRingtones? in + let reader = BufferReader(buffer) + var result: Api.account.SavedRingtones? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.SavedRingtones + } + return result + }) + } +} +public extension Api.functions.account { + static func getSecureValue(types: [Api.SecureValueType]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.SecureValue]>) { + let buffer = Buffer() + buffer.appendInt32(1936088002) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(types.count)) + for item in types { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "account.getSecureValue", parameters: [("types", String(describing: types))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.SecureValue]? in + let reader = BufferReader(buffer) + var result: [Api.SecureValue]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureValue.self) + } + return result + }) + } +} +public extension Api.functions.account { + static func getTheme(format: String, theme: Api.InputTheme) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(978872812) + serializeString(format, buffer: buffer, boxed: false) + theme.serialize(buffer, true) + return (FunctionDescription(name: "account.getTheme", parameters: [("format", String(describing: format)), ("theme", String(describing: theme))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Theme? in + let reader = BufferReader(buffer) + var result: Api.Theme? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Theme + } + return result + }) + } +} +public extension Api.functions.account { + static func getThemes(format: String, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1913054296) + serializeString(format, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getThemes", parameters: [("format", String(describing: format)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.Themes? in + let reader = BufferReader(buffer) + var result: Api.account.Themes? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.Themes + } + return result + }) + } +} +public extension Api.functions.account { + static func getTmpPassword(password: Api.InputCheckPasswordSRP, period: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1151208273) + password.serialize(buffer, true) + serializeInt32(period, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getTmpPassword", parameters: [("password", String(describing: password)), ("period", String(describing: period))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.TmpPassword? in + let reader = BufferReader(buffer) + var result: Api.account.TmpPassword? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.TmpPassword + } + return result + }) + } +} +public extension Api.functions.account { + static func getWallPaper(wallpaper: Api.InputWallPaper) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-57811990) + wallpaper.serialize(buffer, true) + return (FunctionDescription(name: "account.getWallPaper", parameters: [("wallpaper", String(describing: wallpaper))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.WallPaper? in + let reader = BufferReader(buffer) + var result: Api.WallPaper? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.WallPaper + } + return result + }) + } +} +public extension Api.functions.account { + static func getWallPapers(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(127302966) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getWallPapers", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.WallPapers? in + let reader = BufferReader(buffer) + var result: Api.account.WallPapers? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.WallPapers + } + return result + }) + } +} +public extension Api.functions.account { + static func getWebAuthorizations() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(405695855) + + return (FunctionDescription(name: "account.getWebAuthorizations", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.WebAuthorizations? in + let reader = BufferReader(buffer) + var result: Api.account.WebAuthorizations? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.WebAuthorizations + } + return result + }) + } +} +public extension Api.functions.account { + static func initTakeoutSession(flags: Int32, fileMaxSize: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1896617296) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 5) != 0 {serializeInt64(fileMaxSize!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "account.initTakeoutSession", parameters: [("flags", String(describing: flags)), ("fileMaxSize", String(describing: fileMaxSize))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.Takeout? in + let reader = BufferReader(buffer) + var result: Api.account.Takeout? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.Takeout + } + return result + }) + } +} +public extension Api.functions.account { + static func installTheme(flags: Int32, theme: Api.InputTheme?, format: String?, baseTheme: Api.BaseTheme?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-953697477) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {theme!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(format!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {baseTheme!.serialize(buffer, true)} + return (FunctionDescription(name: "account.installTheme", parameters: [("flags", String(describing: flags)), ("theme", String(describing: theme)), ("format", String(describing: format)), ("baseTheme", String(describing: baseTheme))]), 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 installWallPaper(wallpaper: Api.InputWallPaper, settings: Api.WallPaperSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-18000023) + wallpaper.serialize(buffer, true) + settings.serialize(buffer, true) + return (FunctionDescription(name: "account.installWallPaper", parameters: [("wallpaper", String(describing: wallpaper)), ("settings", String(describing: settings))]), 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 invalidateSignInCodes(codes: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-896866118) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(codes.count)) + for item in codes { + serializeString(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "account.invalidateSignInCodes", parameters: [("codes", String(describing: codes))]), 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 registerDevice(flags: Int32, tokenType: Int32, token: String, appSandbox: Api.Bool, secret: Buffer, otherUids: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-326762118) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(tokenType, buffer: buffer, boxed: false) + serializeString(token, buffer: buffer, boxed: false) + appSandbox.serialize(buffer, true) + serializeBytes(secret, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(otherUids.count)) + for item in otherUids { + serializeInt64(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "account.registerDevice", parameters: [("flags", String(describing: flags)), ("tokenType", String(describing: tokenType)), ("token", String(describing: token)), ("appSandbox", String(describing: appSandbox)), ("secret", String(describing: secret)), ("otherUids", String(describing: otherUids))]), 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 reorderUsernames(order: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-279966037) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order.count)) + for item in order { + serializeString(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "account.reorderUsernames", parameters: [("order", String(describing: order))]), 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 reportPeer(peer: Api.InputPeer, reason: Api.ReportReason, message: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-977650298) + peer.serialize(buffer, true) + reason.serialize(buffer, true) + serializeString(message, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.reportPeer", parameters: [("peer", String(describing: peer)), ("reason", String(describing: reason)), ("message", String(describing: message))]), 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 reportProfilePhoto(peer: Api.InputPeer, photoId: Api.InputPhoto, reason: Api.ReportReason, message: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-91437323) + peer.serialize(buffer, true) + photoId.serialize(buffer, true) + reason.serialize(buffer, true) + serializeString(message, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.reportProfilePhoto", parameters: [("peer", String(describing: peer)), ("photoId", String(describing: photoId)), ("reason", String(describing: reason)), ("message", String(describing: message))]), 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 resendPasswordEmail() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2055154197) + + return (FunctionDescription(name: "account.resendPasswordEmail", parameters: []), 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 resetAuthorization(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-545786948) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.resetAuthorization", parameters: [("hash", String(describing: hash))]), 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 resetNotifySettings() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-612493497) + + return (FunctionDescription(name: "account.resetNotifySettings", parameters: []), 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 resetPassword() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1828139493) + + return (FunctionDescription(name: "account.resetPassword", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.ResetPasswordResult? in + let reader = BufferReader(buffer) + var result: Api.account.ResetPasswordResult? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.ResetPasswordResult + } + return result + }) + } +} +public extension Api.functions.account { + static func resetWallPapers() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1153722364) + + return (FunctionDescription(name: "account.resetWallPapers", parameters: []), 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 resetWebAuthorization(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(755087855) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.resetWebAuthorization", parameters: [("hash", String(describing: hash))]), 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 resetWebAuthorizations() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1747789204) + + return (FunctionDescription(name: "account.resetWebAuthorizations", parameters: []), 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 saveAutoDownloadSettings(flags: Int32, settings: Api.AutoDownloadSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1995661875) + serializeInt32(flags, buffer: buffer, boxed: false) + settings.serialize(buffer, true) + return (FunctionDescription(name: "account.saveAutoDownloadSettings", parameters: [("flags", String(describing: flags)), ("settings", String(describing: settings))]), 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 saveAutoSaveSettings(flags: Int32, peer: Api.InputPeer?, settings: Api.AutoSaveSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-694451359) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 3) != 0 {peer!.serialize(buffer, true)} + settings.serialize(buffer, true) + return (FunctionDescription(name: "account.saveAutoSaveSettings", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("settings", String(describing: settings))]), 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() + buffer.appendInt32(1038768899) + id.serialize(buffer, true) + unsave.serialize(buffer, true) + return (FunctionDescription(name: "account.saveRingtone", parameters: [("id", String(describing: id)), ("unsave", String(describing: unsave))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.SavedRingtone? in + let reader = BufferReader(buffer) + var result: Api.account.SavedRingtone? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.SavedRingtone + } + return result + }) + } +} +public extension Api.functions.account { + static func saveSecureValue(value: Api.InputSecureValue, secureSecretId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1986010339) + value.serialize(buffer, true) + serializeInt64(secureSecretId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.saveSecureValue", parameters: [("value", String(describing: value)), ("secureSecretId", String(describing: secureSecretId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.SecureValue? in + let reader = BufferReader(buffer) + var result: Api.SecureValue? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.SecureValue + } + return result + }) + } +} +public extension Api.functions.account { + static func saveTheme(theme: Api.InputTheme, unsave: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-229175188) + theme.serialize(buffer, true) + unsave.serialize(buffer, true) + return (FunctionDescription(name: "account.saveTheme", parameters: [("theme", String(describing: theme)), ("unsave", String(describing: unsave))]), 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 saveWallPaper(wallpaper: Api.InputWallPaper, unsave: Api.Bool, settings: Api.WallPaperSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1817860919) + wallpaper.serialize(buffer, true) + unsave.serialize(buffer, true) + settings.serialize(buffer, true) + return (FunctionDescription(name: "account.saveWallPaper", parameters: [("wallpaper", String(describing: wallpaper)), ("unsave", String(describing: unsave)), ("settings", String(describing: settings))]), 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 sendChangePhoneCode(phoneNumber: String, settings: Api.CodeSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-2108208411) + serializeString(phoneNumber, buffer: buffer, boxed: false) + settings.serialize(buffer, true) + return (FunctionDescription(name: "account.sendChangePhoneCode", parameters: [("phoneNumber", String(describing: phoneNumber)), ("settings", String(describing: settings))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in + let reader = BufferReader(buffer) + var result: Api.auth.SentCode? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.auth.SentCode + } + return result + }) + } +} +public extension Api.functions.account { + static func sendConfirmPhoneCode(hash: String, settings: Api.CodeSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(457157256) + serializeString(hash, buffer: buffer, boxed: false) + settings.serialize(buffer, true) + return (FunctionDescription(name: "account.sendConfirmPhoneCode", parameters: [("hash", String(describing: hash)), ("settings", String(describing: settings))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in + let reader = BufferReader(buffer) + var result: Api.auth.SentCode? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.auth.SentCode + } + return result + }) + } +} +public extension Api.functions.account { + static func sendVerifyEmailCode(purpose: Api.EmailVerifyPurpose, email: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1730136133) + purpose.serialize(buffer, true) + serializeString(email, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.sendVerifyEmailCode", parameters: [("purpose", String(describing: purpose)), ("email", String(describing: email))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.SentEmailCode? in + let reader = BufferReader(buffer) + var result: Api.account.SentEmailCode? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.SentEmailCode + } + return result + }) + } +} +public extension Api.functions.account { + static func sendVerifyPhoneCode(phoneNumber: String, settings: Api.CodeSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1516022023) + serializeString(phoneNumber, buffer: buffer, boxed: false) + settings.serialize(buffer, true) + return (FunctionDescription(name: "account.sendVerifyPhoneCode", parameters: [("phoneNumber", String(describing: phoneNumber)), ("settings", String(describing: settings))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in + let reader = BufferReader(buffer) + var result: Api.auth.SentCode? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.auth.SentCode + } + return result + }) + } +} +public extension Api.functions.account { + static func setAccountTTL(ttl: Api.AccountDaysTTL) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(608323678) + ttl.serialize(buffer, true) + return (FunctionDescription(name: "account.setAccountTTL", parameters: [("ttl", String(describing: ttl))]), 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 setAuthorizationTTL(authorizationTtlDays: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1081501024) + serializeInt32(authorizationTtlDays, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.setAuthorizationTTL", parameters: [("authorizationTtlDays", String(describing: authorizationTtlDays))]), 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 setContactSignUpNotification(silent: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-806076575) + silent.serialize(buffer, true) + return (FunctionDescription(name: "account.setContactSignUpNotification", parameters: [("silent", String(describing: silent))]), 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 setContentSettings(flags: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1250643605) + serializeInt32(flags, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.setContentSettings", parameters: [("flags", String(describing: flags))]), 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 setGlobalPrivacySettings(settings: Api.GlobalPrivacySettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(517647042) + settings.serialize(buffer, true) + return (FunctionDescription(name: "account.setGlobalPrivacySettings", parameters: [("settings", String(describing: settings))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.GlobalPrivacySettings? in + let reader = BufferReader(buffer) + var result: Api.GlobalPrivacySettings? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.GlobalPrivacySettings + } + return result + }) + } +} +public extension Api.functions.account { + static func setPrivacy(key: Api.InputPrivacyKey, rules: [Api.InputPrivacyRule]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-906486552) + key.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(rules.count)) + for item in rules { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "account.setPrivacy", parameters: [("key", String(describing: key)), ("rules", String(describing: rules))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.PrivacyRules? in + let reader = BufferReader(buffer) + var result: Api.account.PrivacyRules? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.PrivacyRules + } + return result + }) + } +} +public extension Api.functions.account { + static func toggleUsername(username: String, active: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1490465654) + serializeString(username, buffer: buffer, boxed: false) + active.serialize(buffer, true) + return (FunctionDescription(name: "account.toggleUsername", parameters: [("username", String(describing: username)), ("active", String(describing: active))]), 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 unregisterDevice(tokenType: Int32, token: String, otherUids: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1779249670) + serializeInt32(tokenType, buffer: buffer, boxed: false) + serializeString(token, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(otherUids.count)) + for item in otherUids { + serializeInt64(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "account.unregisterDevice", parameters: [("tokenType", String(describing: tokenType)), ("token", String(describing: token)), ("otherUids", String(describing: otherUids))]), 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 updateColor(flags: Int32, color: Int32?, backgroundEmojiId: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2096079197) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {serializeInt32(color!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeInt64(backgroundEmojiId!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "account.updateColor", parameters: [("flags", String(describing: flags)), ("color", String(describing: color)), ("backgroundEmojiId", String(describing: backgroundEmojiId))]), 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 updateDeviceLocked(period: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(954152242) + serializeInt32(period, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.updateDeviceLocked", parameters: [("period", String(describing: period))]), 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 updateEmojiStatus(emojiStatus: Api.EmojiStatus) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-70001045) + emojiStatus.serialize(buffer, true) + return (FunctionDescription(name: "account.updateEmojiStatus", parameters: [("emojiStatus", String(describing: emojiStatus))]), 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 updateNotifySettings(peer: Api.InputNotifyPeer, settings: Api.InputPeerNotifySettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-2067899501) + peer.serialize(buffer, true) + settings.serialize(buffer, true) + return (FunctionDescription(name: "account.updateNotifySettings", parameters: [("peer", String(describing: peer)), ("settings", String(describing: settings))]), 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 updatePasswordSettings(password: Api.InputCheckPasswordSRP, newSettings: Api.account.PasswordInputSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1516564433) + password.serialize(buffer, true) + newSettings.serialize(buffer, true) + return (FunctionDescription(name: "account.updatePasswordSettings", parameters: [("password", String(describing: password)), ("newSettings", String(describing: newSettings))]), 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 updateProfile(flags: Int32, firstName: String?, lastName: String?, about: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2018596725) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(firstName!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(lastName!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(about!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "account.updateProfile", parameters: [("flags", String(describing: flags)), ("firstName", String(describing: firstName)), ("lastName", String(describing: lastName)), ("about", String(describing: about))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.User? in + let reader = BufferReader(buffer) + var result: Api.User? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.User + } + return result + }) + } +} +public extension Api.functions.account { + static func updateStatus(offline: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1713919532) + offline.serialize(buffer, true) + return (FunctionDescription(name: "account.updateStatus", parameters: [("offline", String(describing: offline))]), 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 updateTheme(flags: Int32, format: String, theme: Api.InputTheme, slug: String?, title: String?, document: Api.InputDocument?, settings: [Api.InputThemeSettings]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(737414348) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(format, buffer: buffer, boxed: false) + theme.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeString(slug!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {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) + }} + return (FunctionDescription(name: "account.updateTheme", parameters: [("flags", String(describing: flags)), ("format", String(describing: format)), ("theme", String(describing: theme)), ("slug", String(describing: slug)), ("title", String(describing: title)), ("document", String(describing: document)), ("settings", String(describing: settings))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Theme? in + let reader = BufferReader(buffer) + var result: Api.Theme? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Theme + } + return result + }) + } +} +public extension Api.functions.account { + static func updateUsername(username: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1040964988) + serializeString(username, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.updateUsername", parameters: [("username", String(describing: username))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.User? in + let reader = BufferReader(buffer) + var result: Api.User? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.User + } + return result + }) + } +} +public extension Api.functions.account { + static func uploadRingtone(file: Api.InputFile, fileName: String, mimeType: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-2095414366) + file.serialize(buffer, true) + serializeString(fileName, buffer: buffer, boxed: false) + serializeString(mimeType, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.uploadRingtone", parameters: [("file", String(describing: file)), ("fileName", String(describing: fileName)), ("mimeType", String(describing: mimeType))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Document? in + let reader = BufferReader(buffer) + var result: Api.Document? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Document + } + return result + }) + } +} +public extension Api.functions.account { + static func uploadTheme(flags: Int32, file: Api.InputFile, thumb: Api.InputFile?, fileName: String, mimeType: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(473805619) + serializeInt32(flags, buffer: buffer, boxed: false) + file.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {thumb!.serialize(buffer, true)} + serializeString(fileName, buffer: buffer, boxed: false) + serializeString(mimeType, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.uploadTheme", parameters: [("flags", String(describing: flags)), ("file", String(describing: file)), ("thumb", String(describing: thumb)), ("fileName", String(describing: fileName)), ("mimeType", String(describing: mimeType))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Document? in + let reader = BufferReader(buffer) + var result: Api.Document? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Document + } + return result + }) + } +} +public extension Api.functions.account { + static func uploadWallPaper(flags: Int32, file: Api.InputFile, mimeType: String, settings: Api.WallPaperSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-476410109) + serializeInt32(flags, buffer: buffer, boxed: false) + file.serialize(buffer, true) + serializeString(mimeType, buffer: buffer, boxed: false) + settings.serialize(buffer, true) + return (FunctionDescription(name: "account.uploadWallPaper", parameters: [("flags", String(describing: flags)), ("file", String(describing: file)), ("mimeType", String(describing: mimeType)), ("settings", String(describing: settings))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.WallPaper? in + let reader = BufferReader(buffer) + var result: Api.WallPaper? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.WallPaper + } + return result + }) + } +} +public extension Api.functions.account { + static func verifyEmail(purpose: Api.EmailVerifyPurpose, verification: Api.EmailVerification) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(53322959) + purpose.serialize(buffer, true) + verification.serialize(buffer, true) + return (FunctionDescription(name: "account.verifyEmail", parameters: [("purpose", String(describing: purpose)), ("verification", String(describing: verification))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.EmailVerified? in + let reader = BufferReader(buffer) + var result: Api.account.EmailVerified? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.EmailVerified + } + return result + }) + } +} +public extension Api.functions.account { + static func verifyPhone(phoneNumber: String, phoneCodeHash: String, phoneCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1305716726) + serializeString(phoneNumber, buffer: buffer, boxed: false) + serializeString(phoneCodeHash, buffer: buffer, boxed: false) + serializeString(phoneCode, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.verifyPhone", parameters: [("phoneNumber", String(describing: phoneNumber)), ("phoneCodeHash", String(describing: phoneCodeHash)), ("phoneCode", String(describing: phoneCode))]), 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.auth { + static func acceptLoginToken(token: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-392909491) + serializeBytes(token, buffer: buffer, boxed: false) + return (FunctionDescription(name: "auth.acceptLoginToken", parameters: [("token", String(describing: token))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Authorization? in + let reader = BufferReader(buffer) + var result: Api.Authorization? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Authorization + } + return result + }) + } +} +public extension Api.functions.auth { + static func bindTempAuthKey(permAuthKeyId: Int64, nonce: Int64, expiresAt: Int32, encryptedMessage: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-841733627) + serializeInt64(permAuthKeyId, buffer: buffer, boxed: false) + serializeInt64(nonce, buffer: buffer, boxed: false) + serializeInt32(expiresAt, buffer: buffer, boxed: false) + serializeBytes(encryptedMessage, buffer: buffer, boxed: false) + return (FunctionDescription(name: "auth.bindTempAuthKey", parameters: [("permAuthKeyId", String(describing: permAuthKeyId)), ("nonce", String(describing: nonce)), ("expiresAt", String(describing: expiresAt)), ("encryptedMessage", String(describing: encryptedMessage))]), 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.auth { + static func cancelCode(phoneNumber: String, phoneCodeHash: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(520357240) + serializeString(phoneNumber, buffer: buffer, boxed: false) + serializeString(phoneCodeHash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "auth.cancelCode", parameters: [("phoneNumber", String(describing: phoneNumber)), ("phoneCodeHash", String(describing: phoneCodeHash))]), 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.auth { + static func checkPassword(password: Api.InputCheckPasswordSRP) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-779399914) + password.serialize(buffer, true) + return (FunctionDescription(name: "auth.checkPassword", parameters: [("password", String(describing: password))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.Authorization? in + let reader = BufferReader(buffer) + var result: Api.auth.Authorization? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.auth.Authorization + } + return result + }) + } +} +public extension Api.functions.auth { + static func checkRecoveryPassword(code: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(221691769) + serializeString(code, buffer: buffer, boxed: false) + return (FunctionDescription(name: "auth.checkRecoveryPassword", parameters: [("code", String(describing: code))]), 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.auth { + static func dropTempAuthKeys(exceptAuthKeys: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1907842680) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(exceptAuthKeys.count)) + for item in exceptAuthKeys { + serializeInt64(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "auth.dropTempAuthKeys", parameters: [("exceptAuthKeys", String(describing: exceptAuthKeys))]), 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.auth { + static func exportAuthorization(dcId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-440401971) + serializeInt32(dcId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "auth.exportAuthorization", parameters: [("dcId", String(describing: dcId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.ExportedAuthorization? in + let reader = BufferReader(buffer) + var result: Api.auth.ExportedAuthorization? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.auth.ExportedAuthorization + } + return result + }) + } +} +public extension Api.functions.auth { + static func exportLoginToken(apiId: Int32, apiHash: String, exceptIds: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1210022402) + serializeInt32(apiId, buffer: buffer, boxed: false) + serializeString(apiHash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(exceptIds.count)) + for item in exceptIds { + serializeInt64(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "auth.exportLoginToken", parameters: [("apiId", String(describing: apiId)), ("apiHash", String(describing: apiHash)), ("exceptIds", String(describing: exceptIds))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.LoginToken? in + let reader = BufferReader(buffer) + var result: Api.auth.LoginToken? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.auth.LoginToken + } + return result + }) + } +} +public extension Api.functions.auth { + static func importAuthorization(id: Int64, bytes: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1518699091) + serializeInt64(id, buffer: buffer, boxed: false) + serializeBytes(bytes, buffer: buffer, boxed: false) + return (FunctionDescription(name: "auth.importAuthorization", parameters: [("id", String(describing: id)), ("bytes", String(describing: bytes))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.Authorization? in + let reader = BufferReader(buffer) + var result: Api.auth.Authorization? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.auth.Authorization + } + return result + }) + } +} +public extension Api.functions.auth { + static func importBotAuthorization(flags: Int32, apiId: Int32, apiHash: String, botAuthToken: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1738800940) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(apiId, buffer: buffer, boxed: false) + serializeString(apiHash, buffer: buffer, boxed: false) + serializeString(botAuthToken, buffer: buffer, boxed: false) + return (FunctionDescription(name: "auth.importBotAuthorization", parameters: [("flags", String(describing: flags)), ("apiId", String(describing: apiId)), ("apiHash", String(describing: apiHash)), ("botAuthToken", String(describing: botAuthToken))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.Authorization? in + let reader = BufferReader(buffer) + var result: Api.auth.Authorization? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.auth.Authorization + } + return result + }) + } +} +public extension Api.functions.auth { + static func importLoginToken(token: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1783866140) + serializeBytes(token, buffer: buffer, boxed: false) + return (FunctionDescription(name: "auth.importLoginToken", parameters: [("token", String(describing: token))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.LoginToken? in + let reader = BufferReader(buffer) + var result: Api.auth.LoginToken? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.auth.LoginToken + } + return result + }) + } +} +public extension Api.functions.auth { + static func importWebTokenAuthorization(apiId: Int32, apiHash: String, webAuthToken: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(767062953) + serializeInt32(apiId, buffer: buffer, boxed: false) + serializeString(apiHash, buffer: buffer, boxed: false) + serializeString(webAuthToken, buffer: buffer, boxed: false) + return (FunctionDescription(name: "auth.importWebTokenAuthorization", parameters: [("apiId", String(describing: apiId)), ("apiHash", String(describing: apiHash)), ("webAuthToken", String(describing: webAuthToken))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.Authorization? in + let reader = BufferReader(buffer) + var result: Api.auth.Authorization? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.auth.Authorization + } + return result + }) + } +} +public extension Api.functions.auth { + static func logOut() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1047706137) + + return (FunctionDescription(name: "auth.logOut", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.LoggedOut? in + let reader = BufferReader(buffer) + var result: Api.auth.LoggedOut? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.auth.LoggedOut + } + return result + }) + } +} +public extension Api.functions.auth { + static func recoverPassword(flags: Int32, code: String, newSettings: Api.account.PasswordInputSettings?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(923364464) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(code, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {newSettings!.serialize(buffer, true)} + return (FunctionDescription(name: "auth.recoverPassword", parameters: [("flags", String(describing: flags)), ("code", String(describing: code)), ("newSettings", String(describing: newSettings))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.Authorization? in + let reader = BufferReader(buffer) + var result: Api.auth.Authorization? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.auth.Authorization + } + return result + }) + } +} +public extension Api.functions.auth { + static func requestFirebaseSms(flags: Int32, phoneNumber: String, phoneCodeHash: String, safetyNetToken: String?, iosPushSecret: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1991881904) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(phoneNumber, buffer: buffer, boxed: false) + serializeString(phoneCodeHash, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(safetyNetToken!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(iosPushSecret!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "auth.requestFirebaseSms", parameters: [("flags", String(describing: flags)), ("phoneNumber", String(describing: phoneNumber)), ("phoneCodeHash", String(describing: phoneCodeHash)), ("safetyNetToken", String(describing: safetyNetToken)), ("iosPushSecret", String(describing: iosPushSecret))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + 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.auth { + static func requestPasswordRecovery() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-661144474) + + return (FunctionDescription(name: "auth.requestPasswordRecovery", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.PasswordRecovery? in + let reader = BufferReader(buffer) + var result: Api.auth.PasswordRecovery? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.auth.PasswordRecovery + } + return result + }) + } +} +public extension Api.functions.auth { + static func resendCode(phoneNumber: String, phoneCodeHash: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1056025023) + serializeString(phoneNumber, buffer: buffer, boxed: false) + serializeString(phoneCodeHash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "auth.resendCode", parameters: [("phoneNumber", String(describing: phoneNumber)), ("phoneCodeHash", String(describing: phoneCodeHash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in + let reader = BufferReader(buffer) + var result: Api.auth.SentCode? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.auth.SentCode + } + return result + }) + } +} +public extension Api.functions.auth { + static func resetAuthorizations() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1616179942) + + return (FunctionDescription(name: "auth.resetAuthorizations", parameters: []), 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.auth { + static func resetLoginEmail(phoneNumber: String, phoneCodeHash: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2123760019) + serializeString(phoneNumber, buffer: buffer, boxed: false) + serializeString(phoneCodeHash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "auth.resetLoginEmail", parameters: [("phoneNumber", String(describing: phoneNumber)), ("phoneCodeHash", String(describing: phoneCodeHash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in + let reader = BufferReader(buffer) + var result: Api.auth.SentCode? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.auth.SentCode + } + return result + }) + } +} +public extension Api.functions.auth { + static func sendCode(phoneNumber: String, apiId: Int32, apiHash: String, settings: Api.CodeSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1502141361) + serializeString(phoneNumber, buffer: buffer, boxed: false) + serializeInt32(apiId, buffer: buffer, boxed: false) + serializeString(apiHash, buffer: buffer, boxed: false) + settings.serialize(buffer, true) + return (FunctionDescription(name: "auth.sendCode", parameters: [("phoneNumber", String(describing: phoneNumber)), ("apiId", String(describing: apiId)), ("apiHash", String(describing: apiHash)), ("settings", String(describing: settings))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in + let reader = BufferReader(buffer) + var result: Api.auth.SentCode? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.auth.SentCode + } + return result + }) + } +} +public extension Api.functions.auth { + static func signIn(flags: Int32, phoneNumber: String, phoneCodeHash: String, phoneCode: String?, emailVerification: Api.EmailVerification?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1923962543) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(phoneNumber, buffer: buffer, boxed: false) + serializeString(phoneCodeHash, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(phoneCode!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {emailVerification!.serialize(buffer, true)} + return (FunctionDescription(name: "auth.signIn", parameters: [("flags", String(describing: flags)), ("phoneNumber", String(describing: phoneNumber)), ("phoneCodeHash", String(describing: phoneCodeHash)), ("phoneCode", String(describing: phoneCode)), ("emailVerification", String(describing: emailVerification))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.Authorization? in + let reader = BufferReader(buffer) + var result: Api.auth.Authorization? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.auth.Authorization + } + return result + }) + } +} +public extension Api.functions.auth { + static func signUp(phoneNumber: String, phoneCodeHash: String, firstName: String, lastName: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-2131827673) + serializeString(phoneNumber, buffer: buffer, boxed: false) + serializeString(phoneCodeHash, buffer: buffer, boxed: false) + serializeString(firstName, buffer: buffer, boxed: false) + serializeString(lastName, buffer: buffer, boxed: false) + return (FunctionDescription(name: "auth.signUp", parameters: [("phoneNumber", String(describing: phoneNumber)), ("phoneCodeHash", String(describing: phoneCodeHash)), ("firstName", String(describing: firstName)), ("lastName", String(describing: lastName))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.Authorization? in + let reader = BufferReader(buffer) + var result: Api.auth.Authorization? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.auth.Authorization + } + return result + }) + } +} +public extension Api.functions.bots { + static func allowSendMessage(bot: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-248323089) + bot.serialize(buffer, true) + return (FunctionDescription(name: "bots.allowSendMessage", parameters: [("bot", String(describing: bot))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.bots { + static func answerWebhookJSONQuery(queryId: Int64, data: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-434028723) + serializeInt64(queryId, buffer: buffer, boxed: false) + data.serialize(buffer, true) + return (FunctionDescription(name: "bots.answerWebhookJSONQuery", parameters: [("queryId", String(describing: queryId)), ("data", String(describing: data))]), 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.bots { + static func canSendMessage(bot: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(324662502) + bot.serialize(buffer, true) + return (FunctionDescription(name: "bots.canSendMessage", parameters: [("bot", String(describing: bot))]), 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.bots { + static func getBotCommands(scope: Api.BotCommandScope, langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.BotCommand]>) { + let buffer = Buffer() + buffer.appendInt32(-481554986) + scope.serialize(buffer, true) + serializeString(langCode, buffer: buffer, boxed: false) + return (FunctionDescription(name: "bots.getBotCommands", parameters: [("scope", String(describing: scope)), ("langCode", String(describing: langCode))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.BotCommand]? in + let reader = BufferReader(buffer) + var result: [Api.BotCommand]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.BotCommand.self) + } + return result + }) + } +} +public extension Api.functions.bots { + static func getBotInfo(flags: Int32, bot: Api.InputUser?, langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-589753091) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {bot!.serialize(buffer, true)} + serializeString(langCode, buffer: buffer, boxed: false) + return (FunctionDescription(name: "bots.getBotInfo", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("langCode", String(describing: langCode))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.bots.BotInfo? in + let reader = BufferReader(buffer) + var result: Api.bots.BotInfo? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.bots.BotInfo + } + return result + }) + } +} +public extension Api.functions.bots { + static func getBotMenuButton(userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1671369944) + userId.serialize(buffer, true) + return (FunctionDescription(name: "bots.getBotMenuButton", parameters: [("userId", String(describing: userId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.BotMenuButton? in + let reader = BufferReader(buffer) + var result: Api.BotMenuButton? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.BotMenuButton + } + return result + }) + } +} +public extension Api.functions.bots { + static func invokeWebViewCustomMethod(bot: Api.InputUser, customMethod: String, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(142591463) + bot.serialize(buffer, true) + serializeString(customMethod, buffer: buffer, boxed: false) + params.serialize(buffer, true) + return (FunctionDescription(name: "bots.invokeWebViewCustomMethod", parameters: [("bot", String(describing: bot)), ("customMethod", String(describing: customMethod)), ("params", String(describing: params))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.DataJSON? in + let reader = BufferReader(buffer) + var result: Api.DataJSON? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.DataJSON + } + return result + }) + } +} +public extension Api.functions.bots { + static func reorderUsernames(bot: Api.InputUser, order: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1760972350) + bot.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order.count)) + for item in order { + serializeString(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "bots.reorderUsernames", parameters: [("bot", String(describing: bot)), ("order", String(describing: order))]), 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.bots { + static func resetBotCommands(scope: Api.BotCommandScope, langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1032708345) + scope.serialize(buffer, true) + serializeString(langCode, buffer: buffer, boxed: false) + return (FunctionDescription(name: "bots.resetBotCommands", parameters: [("scope", String(describing: scope)), ("langCode", String(describing: langCode))]), 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.bots { + static func sendCustomRequest(customMethod: String, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1440257555) + serializeString(customMethod, buffer: buffer, boxed: false) + params.serialize(buffer, true) + return (FunctionDescription(name: "bots.sendCustomRequest", parameters: [("customMethod", String(describing: customMethod)), ("params", String(describing: params))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.DataJSON? in + let reader = BufferReader(buffer) + var result: Api.DataJSON? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.DataJSON + } + return result + }) + } +} +public extension Api.functions.bots { + static func setBotBroadcastDefaultAdminRights(adminRights: Api.ChatAdminRights) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2021942497) + adminRights.serialize(buffer, true) + return (FunctionDescription(name: "bots.setBotBroadcastDefaultAdminRights", parameters: [("adminRights", String(describing: adminRights))]), 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.bots { + static func setBotCommands(scope: Api.BotCommandScope, langCode: String, commands: [Api.BotCommand]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(85399130) + scope.serialize(buffer, true) + serializeString(langCode, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(commands.count)) + for item in commands { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "bots.setBotCommands", parameters: [("scope", String(describing: scope)), ("langCode", String(describing: langCode)), ("commands", String(describing: commands))]), 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.bots { + static func setBotGroupDefaultAdminRights(adminRights: Api.ChatAdminRights) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1839281686) + adminRights.serialize(buffer, true) + return (FunctionDescription(name: "bots.setBotGroupDefaultAdminRights", parameters: [("adminRights", String(describing: adminRights))]), 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.bots { + static func setBotInfo(flags: Int32, bot: Api.InputUser?, langCode: String, name: String?, about: String?, description: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(282013987) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {bot!.serialize(buffer, true)} + serializeString(langCode, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 3) != 0 {serializeString(name!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeString(about!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(description!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "bots.setBotInfo", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("langCode", String(describing: langCode)), ("name", String(describing: name)), ("about", String(describing: about)), ("description", String(describing: description))]), 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.bots { + static func setBotMenuButton(userId: Api.InputUser, button: Api.BotMenuButton) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1157944655) + userId.serialize(buffer, true) + button.serialize(buffer, true) + return (FunctionDescription(name: "bots.setBotMenuButton", parameters: [("userId", String(describing: userId)), ("button", String(describing: button))]), 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.bots { + static func toggleUsername(bot: Api.InputUser, username: String, active: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(87861619) + bot.serialize(buffer, true) + serializeString(username, buffer: buffer, boxed: false) + active.serialize(buffer, true) + return (FunctionDescription(name: "bots.toggleUsername", parameters: [("bot", String(describing: bot)), ("username", String(describing: username)), ("active", String(describing: active))]), 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 checkUsername(channel: Api.InputChannel, username: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(283557164) + channel.serialize(buffer, true) + serializeString(username, buffer: buffer, boxed: false) + return (FunctionDescription(name: "channels.checkUsername", parameters: [("channel", String(describing: channel)), ("username", String(describing: username))]), 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 clickSponsoredMessage(channel: Api.InputChannel, randomId: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(414170259) + channel.serialize(buffer, true) + serializeBytes(randomId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "channels.clickSponsoredMessage", parameters: [("channel", String(describing: channel)), ("randomId", String(describing: randomId))]), 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 convertToGigagroup(channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(187239529) + channel.serialize(buffer, true) + return (FunctionDescription(name: "channels.convertToGigagroup", parameters: [("channel", String(describing: channel))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func createChannel(flags: Int32, title: String, about: String, geoPoint: Api.InputGeoPoint?, address: String?, ttlPeriod: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1862244601) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + serializeString(about, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {geoPoint!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(address!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "channels.createChannel", parameters: [("flags", String(describing: flags)), ("title", String(describing: title)), ("about", String(describing: about)), ("geoPoint", String(describing: geoPoint)), ("address", String(describing: address)), ("ttlPeriod", String(describing: ttlPeriod))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func createForumTopic(flags: Int32, channel: Api.InputChannel, title: String, iconColor: Int32?, iconEmojiId: Int64?, randomId: Int64, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-200539612) + serializeInt32(flags, buffer: buffer, boxed: false) + channel.serialize(buffer, true) + serializeString(title, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(iconColor!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {serializeInt64(iconEmojiId!, buffer: buffer, boxed: false)} + serializeInt64(randomId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {sendAs!.serialize(buffer, true)} + return (FunctionDescription(name: "channels.createForumTopic", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("title", String(describing: title)), ("iconColor", String(describing: iconColor)), ("iconEmojiId", String(describing: iconEmojiId)), ("randomId", String(describing: randomId)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func deactivateAllUsernames(channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(170155475) + channel.serialize(buffer, true) + return (FunctionDescription(name: "channels.deactivateAllUsernames", parameters: [("channel", String(describing: channel))]), 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 deleteChannel(channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1072619549) + channel.serialize(buffer, true) + return (FunctionDescription(name: "channels.deleteChannel", parameters: [("channel", String(describing: channel))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func deleteHistory(flags: Int32, channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1683319225) + serializeInt32(flags, buffer: buffer, boxed: false) + channel.serialize(buffer, true) + serializeInt32(maxId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "channels.deleteHistory", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("maxId", String(describing: maxId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func deleteMessages(channel: Api.InputChannel, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-2067661490) + channel.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "channels.deleteMessages", parameters: [("channel", String(describing: channel)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedMessages? in + let reader = BufferReader(buffer) + var result: Api.messages.AffectedMessages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.AffectedMessages + } + return result + }) + } +} +public extension Api.functions.channels { + static func deleteParticipantHistory(channel: Api.InputChannel, participant: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(913655003) + channel.serialize(buffer, true) + participant.serialize(buffer, true) + return (FunctionDescription(name: "channels.deleteParticipantHistory", parameters: [("channel", String(describing: channel)), ("participant", String(describing: participant))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedHistory? in + let reader = BufferReader(buffer) + var result: Api.messages.AffectedHistory? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.AffectedHistory + } + return result + }) + } +} +public extension Api.functions.channels { + static func deleteTopicHistory(channel: Api.InputChannel, topMsgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(876830509) + channel.serialize(buffer, true) + serializeInt32(topMsgId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "channels.deleteTopicHistory", parameters: [("channel", String(describing: channel)), ("topMsgId", String(describing: topMsgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedHistory? in + let reader = BufferReader(buffer) + var result: Api.messages.AffectedHistory? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.AffectedHistory + } + return result + }) + } +} +public extension Api.functions.channels { + static func editAdmin(channel: Api.InputChannel, userId: Api.InputUser, adminRights: Api.ChatAdminRights, rank: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-751007486) + channel.serialize(buffer, true) + userId.serialize(buffer, true) + adminRights.serialize(buffer, true) + serializeString(rank, buffer: buffer, boxed: false) + return (FunctionDescription(name: "channels.editAdmin", parameters: [("channel", String(describing: channel)), ("userId", String(describing: userId)), ("adminRights", String(describing: adminRights)), ("rank", String(describing: rank))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func editBanned(channel: Api.InputChannel, participant: Api.InputPeer, bannedRights: Api.ChatBannedRights) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1763259007) + channel.serialize(buffer, true) + participant.serialize(buffer, true) + bannedRights.serialize(buffer, true) + return (FunctionDescription(name: "channels.editBanned", parameters: [("channel", String(describing: channel)), ("participant", String(describing: participant)), ("bannedRights", String(describing: bannedRights))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func editCreator(channel: Api.InputChannel, userId: Api.InputUser, password: Api.InputCheckPasswordSRP) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1892102881) + channel.serialize(buffer, true) + userId.serialize(buffer, true) + password.serialize(buffer, true) + return (FunctionDescription(name: "channels.editCreator", parameters: [("channel", String(describing: channel)), ("userId", String(describing: userId)), ("password", String(describing: password))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func editForumTopic(flags: Int32, channel: Api.InputChannel, topicId: Int32, title: String?, iconEmojiId: Int64?, closed: Api.Bool?, hidden: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-186670715) + serializeInt32(flags, buffer: buffer, boxed: false) + channel.serialize(buffer, true) + serializeInt32(topicId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt64(iconEmojiId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {closed!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {hidden!.serialize(buffer, true)} + return (FunctionDescription(name: "channels.editForumTopic", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("topicId", String(describing: topicId)), ("title", String(describing: title)), ("iconEmojiId", String(describing: iconEmojiId)), ("closed", String(describing: closed)), ("hidden", String(describing: hidden))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func editLocation(channel: Api.InputChannel, geoPoint: Api.InputGeoPoint, address: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1491484525) + channel.serialize(buffer, true) + geoPoint.serialize(buffer, true) + serializeString(address, buffer: buffer, boxed: false) + return (FunctionDescription(name: "channels.editLocation", parameters: [("channel", String(describing: channel)), ("geoPoint", String(describing: geoPoint)), ("address", String(describing: address))]), 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 editPhoto(channel: Api.InputChannel, photo: Api.InputChatPhoto) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-248621111) + channel.serialize(buffer, true) + photo.serialize(buffer, true) + return (FunctionDescription(name: "channels.editPhoto", parameters: [("channel", String(describing: channel)), ("photo", String(describing: photo))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func editTitle(channel: Api.InputChannel, title: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1450044624) + channel.serialize(buffer, true) + serializeString(title, buffer: buffer, boxed: false) + return (FunctionDescription(name: "channels.editTitle", parameters: [("channel", String(describing: channel)), ("title", String(describing: title))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func exportMessageLink(flags: Int32, channel: Api.InputChannel, id: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-432034325) + serializeInt32(flags, buffer: buffer, boxed: false) + channel.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + return (FunctionDescription(name: "channels.exportMessageLink", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedMessageLink? in + let reader = BufferReader(buffer) + var result: Api.ExportedMessageLink? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.ExportedMessageLink + } + return result + }) + } +} +public extension Api.functions.channels { + static func getAdminLog(flags: Int32, channel: Api.InputChannel, q: String, eventsFilter: Api.ChannelAdminLogEventsFilter?, admins: [Api.InputUser]?, maxId: Int64, minId: Int64, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(870184064) + serializeInt32(flags, buffer: buffer, boxed: false) + channel.serialize(buffer, true) + serializeString(q, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {eventsFilter!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(admins!.count)) + for item in admins! { + item.serialize(buffer, true) + }} + serializeInt64(maxId, buffer: buffer, boxed: false) + serializeInt64(minId, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "channels.getAdminLog", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("q", String(describing: q)), ("eventsFilter", String(describing: eventsFilter)), ("admins", String(describing: admins)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.channels.AdminLogResults? in + let reader = BufferReader(buffer) + var result: Api.channels.AdminLogResults? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.channels.AdminLogResults + } + return result + }) + } +} +public extension Api.functions.channels { + static func getAdminedPublicChannels(flags: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-122669393) + serializeInt32(flags, buffer: buffer, boxed: false) + return (FunctionDescription(name: "channels.getAdminedPublicChannels", parameters: [("flags", String(describing: flags))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Chats? in + let reader = BufferReader(buffer) + var result: Api.messages.Chats? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Chats + } + return result + }) + } +} +public extension Api.functions.channels { + static func getChannelRecommendations(channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-2085155433) + channel.serialize(buffer, true) + return (FunctionDescription(name: "channels.getChannelRecommendations", parameters: [("channel", String(describing: channel))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Chats? in + let reader = BufferReader(buffer) + var result: Api.messages.Chats? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Chats + } + return result + }) + } +} +public extension Api.functions.channels { + static func getChannels(id: [Api.InputChannel]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(176122811) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "channels.getChannels", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Chats? in + let reader = BufferReader(buffer) + var result: Api.messages.Chats? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Chats + } + return result + }) + } +} +public extension Api.functions.channels { + static func getForumTopics(flags: Int32, channel: Api.InputChannel, q: String?, offsetDate: Int32, offsetId: Int32, offsetTopic: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(233136337) + serializeInt32(flags, buffer: buffer, boxed: false) + channel.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeString(q!, buffer: buffer, boxed: false)} + serializeInt32(offsetDate, buffer: buffer, boxed: false) + serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeInt32(offsetTopic, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "channels.getForumTopics", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("q", String(describing: q)), ("offsetDate", String(describing: offsetDate)), ("offsetId", String(describing: offsetId)), ("offsetTopic", String(describing: offsetTopic)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ForumTopics? in + let reader = BufferReader(buffer) + var result: Api.messages.ForumTopics? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.ForumTopics + } + return result + }) + } +} +public extension Api.functions.channels { + static func getForumTopicsByID(channel: Api.InputChannel, topics: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1333584199) + channel.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(topics.count)) + for item in topics { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "channels.getForumTopicsByID", parameters: [("channel", String(describing: channel)), ("topics", String(describing: topics))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ForumTopics? in + let reader = BufferReader(buffer) + var result: Api.messages.ForumTopics? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.ForumTopics + } + return result + }) + } +} +public extension Api.functions.channels { + static func getFullChannel(channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(141781513) + channel.serialize(buffer, true) + return (FunctionDescription(name: "channels.getFullChannel", parameters: [("channel", String(describing: channel))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ChatFull? in + let reader = BufferReader(buffer) + var result: Api.messages.ChatFull? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.ChatFull + } + return result + }) + } +} +public extension Api.functions.channels { + static func getGroupsForDiscussion() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-170208392) + + return (FunctionDescription(name: "channels.getGroupsForDiscussion", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Chats? in + let reader = BufferReader(buffer) + var result: Api.messages.Chats? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Chats + } + return result + }) + } +} +public extension Api.functions.channels { + static func getInactiveChannels() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(300429806) + + return (FunctionDescription(name: "channels.getInactiveChannels", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.InactiveChats? in + let reader = BufferReader(buffer) + var result: Api.messages.InactiveChats? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.InactiveChats + } + return result + }) + } +} +public extension Api.functions.channels { + static func getLeftChannels(offset: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-2092831552) + serializeInt32(offset, buffer: buffer, boxed: false) + return (FunctionDescription(name: "channels.getLeftChannels", parameters: [("offset", String(describing: offset))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Chats? in + let reader = BufferReader(buffer) + var result: Api.messages.Chats? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Chats + } + return result + }) + } +} +public extension Api.functions.channels { + static func getMessages(channel: Api.InputChannel, id: [Api.InputMessage]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1383294429) + channel.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "channels.getMessages", parameters: [("channel", String(describing: channel)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + let reader = BufferReader(buffer) + var result: Api.messages.Messages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Messages + } + return result + }) + } +} +public extension Api.functions.channels { + static func getParticipant(channel: Api.InputChannel, participant: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1599378234) + channel.serialize(buffer, true) + participant.serialize(buffer, true) + return (FunctionDescription(name: "channels.getParticipant", parameters: [("channel", String(describing: channel)), ("participant", String(describing: participant))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.channels.ChannelParticipant? in + let reader = BufferReader(buffer) + var result: Api.channels.ChannelParticipant? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.channels.ChannelParticipant + } + return result + }) + } +} +public extension Api.functions.channels { + static func getParticipants(channel: Api.InputChannel, filter: Api.ChannelParticipantsFilter, offset: Int32, limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2010044880) + channel.serialize(buffer, true) + filter.serialize(buffer, true) + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "channels.getParticipants", parameters: [("channel", String(describing: channel)), ("filter", String(describing: filter)), ("offset", String(describing: offset)), ("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.channels.ChannelParticipants? in + let reader = BufferReader(buffer) + var result: Api.channels.ChannelParticipants? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.channels.ChannelParticipants + } + return result + }) + } +} +public extension Api.functions.channels { + static func getSendAs(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(231174382) + peer.serialize(buffer, true) + return (FunctionDescription(name: "channels.getSendAs", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.channels.SendAsPeers? in + let reader = BufferReader(buffer) + var result: Api.channels.SendAsPeers? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.channels.SendAsPeers + } + return result + }) + } +} +public extension Api.functions.channels { + static func getSponsoredMessages(channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-333377601) + channel.serialize(buffer, true) + return (FunctionDescription(name: "channels.getSponsoredMessages", parameters: [("channel", String(describing: channel))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SponsoredMessages? in + let reader = BufferReader(buffer) + var result: Api.messages.SponsoredMessages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.SponsoredMessages + } + return result + }) + } +} +public extension Api.functions.channels { + static func inviteToChannel(channel: Api.InputChannel, users: [Api.InputUser]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(429865580) + channel.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "channels.inviteToChannel", parameters: [("channel", String(describing: channel)), ("users", String(describing: users))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func joinChannel(channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(615851205) + channel.serialize(buffer, true) + return (FunctionDescription(name: "channels.joinChannel", parameters: [("channel", String(describing: channel))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func leaveChannel(channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-130635115) + channel.serialize(buffer, true) + return (FunctionDescription(name: "channels.leaveChannel", parameters: [("channel", String(describing: channel))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-871347913) + channel.serialize(buffer, true) + serializeInt32(maxId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "channels.readHistory", parameters: [("channel", String(describing: channel)), ("maxId", String(describing: maxId))]), 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 readMessageContents(channel: Api.InputChannel, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-357180360) + channel.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "channels.readMessageContents", parameters: [("channel", String(describing: channel)), ("id", String(describing: id))]), 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 reorderPinnedForumTopics(flags: Int32, channel: Api.InputChannel, order: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(693150095) + serializeInt32(flags, buffer: buffer, boxed: false) + channel.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order.count)) + for item in order { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "channels.reorderPinnedForumTopics", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("order", String(describing: order))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func reorderUsernames(channel: Api.InputChannel, order: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1268978403) + channel.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order.count)) + for item in order { + serializeString(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "channels.reorderUsernames", parameters: [("channel", String(describing: channel)), ("order", String(describing: order))]), 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 reportAntiSpamFalsePositive(channel: Api.InputChannel, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1471109485) + channel.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "channels.reportAntiSpamFalsePositive", parameters: [("channel", String(describing: channel)), ("msgId", String(describing: msgId))]), 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 reportSpam(channel: Api.InputChannel, participant: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-196443371) + channel.serialize(buffer, true) + participant.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "channels.reportSpam", parameters: [("channel", String(describing: channel)), ("participant", String(describing: participant)), ("id", String(describing: id))]), 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 setDiscussionGroup(broadcast: Api.InputChannel, group: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1079520178) + broadcast.serialize(buffer, true) + group.serialize(buffer, true) + return (FunctionDescription(name: "channels.setDiscussionGroup", parameters: [("broadcast", String(describing: broadcast)), ("group", String(describing: group))]), 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() + buffer.appendInt32(-359881479) + channel.serialize(buffer, true) + stickerset.serialize(buffer, true) + return (FunctionDescription(name: "channels.setStickers", parameters: [("channel", String(describing: channel)), ("stickerset", String(describing: stickerset))]), 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 toggleAntiSpam(channel: Api.InputChannel, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1760814315) + channel.serialize(buffer, true) + enabled.serialize(buffer, true) + return (FunctionDescription(name: "channels.toggleAntiSpam", parameters: [("channel", String(describing: channel)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func toggleForum(channel: Api.InputChannel, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1540781271) + channel.serialize(buffer, true) + enabled.serialize(buffer, true) + return (FunctionDescription(name: "channels.toggleForum", parameters: [("channel", String(describing: channel)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func toggleJoinRequest(channel: Api.InputChannel, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1277789622) + channel.serialize(buffer, true) + enabled.serialize(buffer, true) + return (FunctionDescription(name: "channels.toggleJoinRequest", parameters: [("channel", String(describing: channel)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func toggleJoinToSend(channel: Api.InputChannel, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-456419968) + channel.serialize(buffer, true) + enabled.serialize(buffer, true) + return (FunctionDescription(name: "channels.toggleJoinToSend", parameters: [("channel", String(describing: channel)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func toggleParticipantsHidden(channel: Api.InputChannel, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1785624660) + channel.serialize(buffer, true) + enabled.serialize(buffer, true) + return (FunctionDescription(name: "channels.toggleParticipantsHidden", parameters: [("channel", String(describing: channel)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func togglePreHistoryHidden(channel: Api.InputChannel, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-356796084) + channel.serialize(buffer, true) + enabled.serialize(buffer, true) + return (FunctionDescription(name: "channels.togglePreHistoryHidden", parameters: [("channel", String(describing: channel)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func toggleSignatures(channel: Api.InputChannel, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(527021574) + channel.serialize(buffer, true) + enabled.serialize(buffer, true) + return (FunctionDescription(name: "channels.toggleSignatures", parameters: [("channel", String(describing: channel)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func toggleSlowMode(channel: Api.InputChannel, seconds: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-304832784) + channel.serialize(buffer, true) + serializeInt32(seconds, buffer: buffer, boxed: false) + return (FunctionDescription(name: "channels.toggleSlowMode", parameters: [("channel", String(describing: channel)), ("seconds", String(describing: seconds))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func toggleUsername(channel: Api.InputChannel, username: String, active: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1358053637) + channel.serialize(buffer, true) + serializeString(username, buffer: buffer, boxed: false) + active.serialize(buffer, true) + return (FunctionDescription(name: "channels.toggleUsername", parameters: [("channel", String(describing: channel)), ("username", String(describing: username)), ("active", String(describing: active))]), 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 toggleViewForumAsMessages(channel: Api.InputChannel, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1757889771) + channel.serialize(buffer, true) + enabled.serialize(buffer, true) + return (FunctionDescription(name: "channels.toggleViewForumAsMessages", parameters: [("channel", String(describing: channel)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func updateColor(flags: Int32, channel: Api.InputChannel, color: Int32, backgroundEmojiId: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1645879327) + serializeInt32(flags, buffer: buffer, boxed: false) + channel.serialize(buffer, true) + serializeInt32(color, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt64(backgroundEmojiId!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "channels.updateColor", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("color", String(describing: color)), ("backgroundEmojiId", String(describing: backgroundEmojiId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func updatePinnedForumTopic(channel: Api.InputChannel, topicId: Int32, pinned: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1814925350) + channel.serialize(buffer, true) + serializeInt32(topicId, buffer: buffer, boxed: false) + pinned.serialize(buffer, true) + return (FunctionDescription(name: "channels.updatePinnedForumTopic", parameters: [("channel", String(describing: channel)), ("topicId", String(describing: topicId)), ("pinned", String(describing: pinned))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.channels { + static func updateUsername(channel: Api.InputChannel, username: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(890549214) + channel.serialize(buffer, true) + serializeString(username, buffer: buffer, boxed: false) + return (FunctionDescription(name: "channels.updateUsername", parameters: [("channel", String(describing: channel)), ("username", String(describing: username))]), 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 viewSponsoredMessage(channel: Api.InputChannel, randomId: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1095836780) + channel.serialize(buffer, true) + serializeBytes(randomId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "channels.viewSponsoredMessage", parameters: [("channel", String(describing: channel)), ("randomId", String(describing: randomId))]), 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.chatlists { + static func checkChatlistInvite(slug: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1103171583) + serializeString(slug, buffer: buffer, boxed: false) + return (FunctionDescription(name: "chatlists.checkChatlistInvite", parameters: [("slug", String(describing: slug))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.chatlists.ChatlistInvite? in + let reader = BufferReader(buffer) + var result: Api.chatlists.ChatlistInvite? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.chatlists.ChatlistInvite + } + return result + }) + } +} +public extension Api.functions.chatlists { + static func deleteExportedInvite(chatlist: Api.InputChatlist, slug: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1906072670) + chatlist.serialize(buffer, true) + serializeString(slug, buffer: buffer, boxed: false) + return (FunctionDescription(name: "chatlists.deleteExportedInvite", parameters: [("chatlist", String(describing: chatlist)), ("slug", String(describing: slug))]), 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.chatlists { + static func editExportedInvite(flags: Int32, chatlist: Api.InputChatlist, slug: String, title: String?, peers: [Api.InputPeer]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1698543165) + serializeInt32(flags, buffer: buffer, boxed: false) + chatlist.serialize(buffer, true) + serializeString(slug, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peers!.count)) + for item in peers! { + item.serialize(buffer, true) + }} + return (FunctionDescription(name: "chatlists.editExportedInvite", parameters: [("flags", String(describing: flags)), ("chatlist", String(describing: chatlist)), ("slug", String(describing: slug)), ("title", String(describing: title)), ("peers", String(describing: peers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedChatlistInvite? in + let reader = BufferReader(buffer) + var result: Api.ExportedChatlistInvite? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.ExportedChatlistInvite + } + return result + }) + } +} +public extension Api.functions.chatlists { + static func exportChatlistInvite(chatlist: Api.InputChatlist, title: String, peers: [Api.InputPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-2072885362) + chatlist.serialize(buffer, true) + serializeString(title, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peers.count)) + for item in peers { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "chatlists.exportChatlistInvite", parameters: [("chatlist", String(describing: chatlist)), ("title", String(describing: title)), ("peers", String(describing: peers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.chatlists.ExportedChatlistInvite? in + let reader = BufferReader(buffer) + var result: Api.chatlists.ExportedChatlistInvite? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.chatlists.ExportedChatlistInvite + } + return result + }) + } +} +public extension Api.functions.chatlists { + static func getChatlistUpdates(chatlist: Api.InputChatlist) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1992190687) + chatlist.serialize(buffer, true) + return (FunctionDescription(name: "chatlists.getChatlistUpdates", parameters: [("chatlist", String(describing: chatlist))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.chatlists.ChatlistUpdates? in + let reader = BufferReader(buffer) + var result: Api.chatlists.ChatlistUpdates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.chatlists.ChatlistUpdates + } + return result + }) + } +} +public extension Api.functions.chatlists { + static func getExportedInvites(chatlist: Api.InputChatlist) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-838608253) + chatlist.serialize(buffer, true) + return (FunctionDescription(name: "chatlists.getExportedInvites", parameters: [("chatlist", String(describing: chatlist))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.chatlists.ExportedInvites? in + let reader = BufferReader(buffer) + var result: Api.chatlists.ExportedInvites? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.chatlists.ExportedInvites + } + return result + }) + } +} +public extension Api.functions.chatlists { + static func getLeaveChatlistSuggestions(chatlist: Api.InputChatlist) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.Peer]>) { + let buffer = Buffer() + buffer.appendInt32(-37955820) + chatlist.serialize(buffer, true) + return (FunctionDescription(name: "chatlists.getLeaveChatlistSuggestions", parameters: [("chatlist", String(describing: chatlist))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.Peer]? in + let reader = BufferReader(buffer) + var result: [Api.Peer]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) + } + return result + }) + } +} +public extension Api.functions.chatlists { + static func hideChatlistUpdates(chatlist: Api.InputChatlist) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1726252795) + chatlist.serialize(buffer, true) + return (FunctionDescription(name: "chatlists.hideChatlistUpdates", parameters: [("chatlist", String(describing: chatlist))]), 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.chatlists { + static func joinChatlistInvite(slug: String, peers: [Api.InputPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1498291302) + serializeString(slug, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peers.count)) + for item in peers { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "chatlists.joinChatlistInvite", parameters: [("slug", String(describing: slug)), ("peers", String(describing: peers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.chatlists { + static func joinChatlistUpdates(chatlist: Api.InputChatlist, peers: [Api.InputPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-527828747) + chatlist.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peers.count)) + for item in peers { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "chatlists.joinChatlistUpdates", parameters: [("chatlist", String(describing: chatlist)), ("peers", String(describing: peers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.chatlists { + static func leaveChatlist(chatlist: Api.InputChatlist, peers: [Api.InputPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1962598714) + chatlist.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peers.count)) + for item in peers { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "chatlists.leaveChatlist", parameters: [("chatlist", String(describing: chatlist)), ("peers", String(describing: peers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.contacts { + static func acceptContact(id: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-130964977) + id.serialize(buffer, true) + return (FunctionDescription(name: "contacts.acceptContact", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.contacts { + static func addContact(flags: Int32, id: Api.InputUser, firstName: String, lastName: String, phone: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-386636848) + serializeInt32(flags, buffer: buffer, boxed: false) + id.serialize(buffer, true) + serializeString(firstName, buffer: buffer, boxed: false) + serializeString(lastName, buffer: buffer, boxed: false) + serializeString(phone, buffer: buffer, boxed: false) + return (FunctionDescription(name: "contacts.addContact", parameters: [("flags", String(describing: flags)), ("id", String(describing: id)), ("firstName", String(describing: firstName)), ("lastName", String(describing: lastName)), ("phone", String(describing: phone))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.contacts { + static func block(flags: Int32, id: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(774801204) + serializeInt32(flags, buffer: buffer, boxed: false) + id.serialize(buffer, true) + return (FunctionDescription(name: "contacts.block", parameters: [("flags", String(describing: flags)), ("id", String(describing: id))]), 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.contacts { + static func blockFromReplies(flags: Int32, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(698914348) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(msgId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "contacts.blockFromReplies", parameters: [("flags", String(describing: flags)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.contacts { + static func deleteByPhones(phones: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(269745566) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(phones.count)) + for item in phones { + serializeString(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "contacts.deleteByPhones", parameters: [("phones", String(describing: phones))]), 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.contacts { + static func deleteContacts(id: [Api.InputUser]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(157945344) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "contacts.deleteContacts", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.contacts { + static func editCloseFriends(id: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1167653392) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt64(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "contacts.editCloseFriends", parameters: [("id", String(describing: id))]), 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.contacts { + static func exportContactToken() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-127582169) + + return (FunctionDescription(name: "contacts.exportContactToken", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedContactToken? in + let reader = BufferReader(buffer) + var result: Api.ExportedContactToken? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.ExportedContactToken + } + return result + }) + } +} +public extension Api.functions.contacts { + static func getBlocked(flags: Int32, offset: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1702457472) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "contacts.getBlocked", parameters: [("flags", String(describing: flags)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.contacts.Blocked? in + let reader = BufferReader(buffer) + var result: Api.contacts.Blocked? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.contacts.Blocked + } + return result + }) + } +} +public extension Api.functions.contacts { + static func getContactIDs(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int32]>) { + let buffer = Buffer() + buffer.appendInt32(2061264541) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "contacts.getContactIDs", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int32]? in + let reader = BufferReader(buffer) + var result: [Int32]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + return result + }) + } +} +public extension Api.functions.contacts { + static func getContacts(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1574346258) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "contacts.getContacts", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.contacts.Contacts? in + let reader = BufferReader(buffer) + var result: Api.contacts.Contacts? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.contacts.Contacts + } + return result + }) + } +} +public extension Api.functions.contacts { + static func getLocated(flags: Int32, geoPoint: Api.InputGeoPoint, selfExpires: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-750207932) + serializeInt32(flags, buffer: buffer, boxed: false) + geoPoint.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(selfExpires!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "contacts.getLocated", parameters: [("flags", String(describing: flags)), ("geoPoint", String(describing: geoPoint)), ("selfExpires", String(describing: selfExpires))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.contacts { + static func getSaved() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.SavedContact]>) { + let buffer = Buffer() + buffer.appendInt32(-2098076769) + + return (FunctionDescription(name: "contacts.getSaved", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.SavedContact]? in + let reader = BufferReader(buffer) + var result: [Api.SavedContact]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.SavedContact.self) + } + return result + }) + } +} +public extension Api.functions.contacts { + static func getStatuses() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.ContactStatus]>) { + let buffer = Buffer() + buffer.appendInt32(-995929106) + + return (FunctionDescription(name: "contacts.getStatuses", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.ContactStatus]? in + let reader = BufferReader(buffer) + var result: [Api.ContactStatus]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.ContactStatus.self) + } + return result + }) + } +} +public extension Api.functions.contacts { + static func getTopPeers(flags: Int32, offset: Int32, limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1758168906) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "contacts.getTopPeers", parameters: [("flags", String(describing: flags)), ("offset", String(describing: offset)), ("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.contacts.TopPeers? in + let reader = BufferReader(buffer) + var result: Api.contacts.TopPeers? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.contacts.TopPeers + } + return result + }) + } +} +public extension Api.functions.contacts { + static func importContactToken(token: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(318789512) + serializeString(token, buffer: buffer, boxed: false) + return (FunctionDescription(name: "contacts.importContactToken", parameters: [("token", String(describing: token))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.User? in + let reader = BufferReader(buffer) + var result: Api.User? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.User + } + return result + }) + } +} +public extension Api.functions.contacts { + static func importContacts(contacts: [Api.InputContact]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(746589157) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(contacts.count)) + for item in contacts { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "contacts.importContacts", parameters: [("contacts", String(describing: contacts))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.contacts.ImportedContacts? in + let reader = BufferReader(buffer) + var result: Api.contacts.ImportedContacts? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.contacts.ImportedContacts + } + return result + }) + } +} +public extension Api.functions.contacts { + static func resetSaved() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-2020263951) + + return (FunctionDescription(name: "contacts.resetSaved", parameters: []), 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.contacts { + static func resetTopPeerRating(category: Api.TopPeerCategory, peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(451113900) + category.serialize(buffer, true) + peer.serialize(buffer, true) + return (FunctionDescription(name: "contacts.resetTopPeerRating", parameters: [("category", String(describing: category)), ("peer", String(describing: peer))]), 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.contacts { + static func resolvePhone(phone: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1963375804) + serializeString(phone, buffer: buffer, boxed: false) + return (FunctionDescription(name: "contacts.resolvePhone", parameters: [("phone", String(describing: phone))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.contacts.ResolvedPeer? in + let reader = BufferReader(buffer) + var result: Api.contacts.ResolvedPeer? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.contacts.ResolvedPeer + } + return result + }) + } +} +public extension Api.functions.contacts { + static func resolveUsername(username: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-113456221) + serializeString(username, buffer: buffer, boxed: false) + return (FunctionDescription(name: "contacts.resolveUsername", parameters: [("username", String(describing: username))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.contacts.ResolvedPeer? in + let reader = BufferReader(buffer) + var result: Api.contacts.ResolvedPeer? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.contacts.ResolvedPeer + } + return result + }) + } +} +public extension Api.functions.contacts { + static func search(q: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(301470424) + serializeString(q, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "contacts.search", parameters: [("q", String(describing: q)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.contacts.Found? in + let reader = BufferReader(buffer) + var result: Api.contacts.Found? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.contacts.Found + } + return result + }) + } +} +public extension Api.functions.contacts { + static func setBlocked(flags: Int32, id: [Api.InputPeer], limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1798939530) + serializeInt32(flags, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + item.serialize(buffer, true) + } + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "contacts.setBlocked", parameters: [("flags", String(describing: flags)), ("id", String(describing: id)), ("limit", String(describing: limit))]), 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.contacts { + static func toggleTopPeers(enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-2062238246) + enabled.serialize(buffer, true) + return (FunctionDescription(name: "contacts.toggleTopPeers", parameters: [("enabled", String(describing: enabled))]), 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.contacts { + static func unblock(flags: Int32, id: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1252994264) + serializeInt32(flags, buffer: buffer, boxed: false) + id.serialize(buffer, true) + return (FunctionDescription(name: "contacts.unblock", parameters: [("flags", String(describing: flags)), ("id", String(describing: id))]), 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.folders { + static func editPeerFolders(folderPeers: [Api.InputFolderPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1749536939) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(folderPeers.count)) + for item in folderPeers { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "folders.editPeerFolders", parameters: [("folderPeers", String(describing: folderPeers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.help { + static func acceptTermsOfService(id: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-294455398) + id.serialize(buffer, true) + return (FunctionDescription(name: "help.acceptTermsOfService", parameters: [("id", String(describing: id))]), 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.help { + static func dismissSuggestion(peer: Api.InputPeer, suggestion: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-183649631) + peer.serialize(buffer, true) + serializeString(suggestion, buffer: buffer, boxed: false) + return (FunctionDescription(name: "help.dismissSuggestion", parameters: [("peer", String(describing: peer)), ("suggestion", String(describing: suggestion))]), 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.help { + static func editUserInfo(userId: Api.InputUser, message: String, entities: [Api.MessageEntity]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1723407216) + userId.serialize(buffer, true) + serializeString(message, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities.count)) + for item in entities { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "help.editUserInfo", parameters: [("userId", String(describing: userId)), ("message", String(describing: message)), ("entities", String(describing: entities))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.UserInfo? in + let reader = BufferReader(buffer) + var result: Api.help.UserInfo? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.UserInfo + } + return result + }) + } +} +public extension Api.functions.help { + static func getAppChangelog(prevAppVersion: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1877938321) + serializeString(prevAppVersion, buffer: buffer, boxed: false) + return (FunctionDescription(name: "help.getAppChangelog", parameters: [("prevAppVersion", String(describing: prevAppVersion))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.help { + static func getAppConfig(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1642330196) + serializeInt32(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "help.getAppConfig", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.AppConfig? in + let reader = BufferReader(buffer) + var result: Api.help.AppConfig? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.AppConfig + } + return result + }) + } +} +public extension Api.functions.help { + static func getAppUpdate(source: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1378703997) + serializeString(source, buffer: buffer, boxed: false) + return (FunctionDescription(name: "help.getAppUpdate", parameters: [("source", String(describing: source))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.AppUpdate? in + let reader = BufferReader(buffer) + var result: Api.help.AppUpdate? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.AppUpdate + } + return result + }) + } +} +public extension Api.functions.help { + static func getCdnConfig() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1375900482) + + return (FunctionDescription(name: "help.getCdnConfig", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.CdnConfig? in + let reader = BufferReader(buffer) + var result: Api.CdnConfig? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.CdnConfig + } + return result + }) + } +} +public extension Api.functions.help { + static func getConfig() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-990308245) + + return (FunctionDescription(name: "help.getConfig", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Config? in + let reader = BufferReader(buffer) + var result: Api.Config? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Config + } + return result + }) + } +} +public extension Api.functions.help { + static func getCountriesList(langCode: String, hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1935116200) + serializeString(langCode, buffer: buffer, boxed: false) + serializeInt32(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "help.getCountriesList", parameters: [("langCode", String(describing: langCode)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.CountriesList? in + let reader = BufferReader(buffer) + var result: Api.help.CountriesList? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.CountriesList + } + return result + }) + } +} +public extension Api.functions.help { + static func getDeepLinkInfo(path: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1072547679) + serializeString(path, buffer: buffer, boxed: false) + return (FunctionDescription(name: "help.getDeepLinkInfo", parameters: [("path", String(describing: path))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.DeepLinkInfo? in + let reader = BufferReader(buffer) + var result: Api.help.DeepLinkInfo? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.DeepLinkInfo + } + return result + }) + } +} +public extension Api.functions.help { + static func getInviteText() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1295590211) + + return (FunctionDescription(name: "help.getInviteText", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.InviteText? in + let reader = BufferReader(buffer) + var result: Api.help.InviteText? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.InviteText + } + return result + }) + } +} +public extension Api.functions.help { + static func getNearestDc() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(531836966) + + return (FunctionDescription(name: "help.getNearestDc", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.NearestDc? in + let reader = BufferReader(buffer) + var result: Api.NearestDc? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.NearestDc + } + return result + }) + } +} +public extension Api.functions.help { + static func getPassportConfig(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-966677240) + serializeInt32(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "help.getPassportConfig", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.PassportConfig? in + let reader = BufferReader(buffer) + var result: Api.help.PassportConfig? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.PassportConfig + } + return result + }) + } +} +public extension Api.functions.help { + static func getPeerColors(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-629083089) + serializeInt32(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "help.getPeerColors", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.PeerColors? in + let reader = BufferReader(buffer) + var result: Api.help.PeerColors? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.PeerColors + } + return result + }) + } +} +public extension Api.functions.help { + static func getPeerProfileColors(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1412453891) + serializeInt32(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "help.getPeerProfileColors", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.PeerColors? in + let reader = BufferReader(buffer) + var result: Api.help.PeerColors? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.PeerColors + } + return result + }) + } +} +public extension Api.functions.help { + static func getPremiumPromo() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1206152236) + + return (FunctionDescription(name: "help.getPremiumPromo", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.PremiumPromo? in + let reader = BufferReader(buffer) + var result: Api.help.PremiumPromo? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.PremiumPromo + } + return result + }) + } +} +public extension Api.functions.help { + static func getPromoData() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1063816159) + + return (FunctionDescription(name: "help.getPromoData", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.PromoData? in + let reader = BufferReader(buffer) + var result: Api.help.PromoData? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.PromoData + } + return result + }) + } +} +public extension Api.functions.help { + static func getRecentMeUrls(referer: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1036054804) + serializeString(referer, buffer: buffer, boxed: false) + return (FunctionDescription(name: "help.getRecentMeUrls", parameters: [("referer", String(describing: referer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.RecentMeUrls? in + let reader = BufferReader(buffer) + var result: Api.help.RecentMeUrls? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.RecentMeUrls + } + return result + }) + } +} +public extension Api.functions.help { + static func getSupport() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1663104819) + + return (FunctionDescription(name: "help.getSupport", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.Support? in + let reader = BufferReader(buffer) + var result: Api.help.Support? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.Support + } + return result + }) + } +} +public extension Api.functions.help { + static func getSupportName() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-748624084) + + return (FunctionDescription(name: "help.getSupportName", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.SupportName? in + let reader = BufferReader(buffer) + var result: Api.help.SupportName? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.SupportName + } + return result + }) + } +} +public extension Api.functions.help { + static func getTermsOfServiceUpdate() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(749019089) + + return (FunctionDescription(name: "help.getTermsOfServiceUpdate", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.TermsOfServiceUpdate? in + let reader = BufferReader(buffer) + var result: Api.help.TermsOfServiceUpdate? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.TermsOfServiceUpdate + } + return result + }) + } +} +public extension Api.functions.help { + static func getUserInfo(userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(59377875) + userId.serialize(buffer, true) + return (FunctionDescription(name: "help.getUserInfo", parameters: [("userId", String(describing: userId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.UserInfo? in + let reader = BufferReader(buffer) + var result: Api.help.UserInfo? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.UserInfo + } + return result + }) + } +} +public extension Api.functions.help { + static func hidePromoData(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(505748629) + peer.serialize(buffer, true) + return (FunctionDescription(name: "help.hidePromoData", parameters: [("peer", String(describing: peer))]), 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.help { + static func saveAppLog(events: [Api.InputAppEvent]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1862465352) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(events.count)) + for item in events { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "help.saveAppLog", parameters: [("events", String(describing: events))]), 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.help { + static func setBotUpdatesStatus(pendingUpdatesCount: Int32, message: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-333262899) + serializeInt32(pendingUpdatesCount, buffer: buffer, boxed: false) + serializeString(message, buffer: buffer, boxed: false) + return (FunctionDescription(name: "help.setBotUpdatesStatus", parameters: [("pendingUpdatesCount", String(describing: pendingUpdatesCount)), ("message", String(describing: message))]), 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.help { + static func test() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1058929929) + + return (FunctionDescription(name: "help.test", parameters: []), 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.langpack { + static func getDifference(langPack: String, langCode: String, fromVersion: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-845657435) + serializeString(langPack, buffer: buffer, boxed: false) + serializeString(langCode, buffer: buffer, boxed: false) + serializeInt32(fromVersion, buffer: buffer, boxed: false) + return (FunctionDescription(name: "langpack.getDifference", parameters: [("langPack", String(describing: langPack)), ("langCode", String(describing: langCode)), ("fromVersion", String(describing: fromVersion))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.LangPackDifference? in + let reader = BufferReader(buffer) + var result: Api.LangPackDifference? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.LangPackDifference + } + return result + }) + } +} +public extension Api.functions.langpack { + static func getLangPack(langPack: String, langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-219008246) + serializeString(langPack, buffer: buffer, boxed: false) + serializeString(langCode, buffer: buffer, boxed: false) + return (FunctionDescription(name: "langpack.getLangPack", parameters: [("langPack", String(describing: langPack)), ("langCode", String(describing: langCode))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.LangPackDifference? in + let reader = BufferReader(buffer) + var result: Api.LangPackDifference? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.LangPackDifference + } + return result + }) + } +} +public extension Api.functions.langpack { + static func getLanguage(langPack: String, langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1784243458) + serializeString(langPack, buffer: buffer, boxed: false) + serializeString(langCode, buffer: buffer, boxed: false) + return (FunctionDescription(name: "langpack.getLanguage", parameters: [("langPack", String(describing: langPack)), ("langCode", String(describing: langCode))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.LangPackLanguage? in + let reader = BufferReader(buffer) + var result: Api.LangPackLanguage? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.LangPackLanguage + } + return result + }) + } +} +public extension Api.functions.langpack { + static func getLanguages(langPack: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.LangPackLanguage]>) { + let buffer = Buffer() + buffer.appendInt32(1120311183) + serializeString(langPack, buffer: buffer, boxed: false) + return (FunctionDescription(name: "langpack.getLanguages", parameters: [("langPack", String(describing: langPack))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.LangPackLanguage]? in + let reader = BufferReader(buffer) + var result: [Api.LangPackLanguage]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.LangPackLanguage.self) + } + return result + }) + } +} +public extension Api.functions.langpack { + static func getStrings(langPack: String, langCode: String, keys: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.LangPackString]>) { + let buffer = Buffer() + buffer.appendInt32(-269862909) + serializeString(langPack, buffer: buffer, boxed: false) + serializeString(langCode, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(keys.count)) + for item in keys { + serializeString(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "langpack.getStrings", parameters: [("langPack", String(describing: langPack)), ("langCode", String(describing: langCode)), ("keys", String(describing: keys))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.LangPackString]? in + let reader = BufferReader(buffer) + var result: [Api.LangPackString]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.LangPackString.self) + } + return result + }) + } +} +public extension Api.functions.messages { + static func acceptEncryption(peer: Api.InputEncryptedChat, gB: Buffer, keyFingerprint: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1035731989) + peer.serialize(buffer, true) + serializeBytes(gB, buffer: buffer, boxed: false) + serializeInt64(keyFingerprint, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.acceptEncryption", parameters: [("peer", String(describing: peer)), ("gB", String(describing: gB)), ("keyFingerprint", String(describing: keyFingerprint))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EncryptedChat? in + let reader = BufferReader(buffer) + var result: Api.EncryptedChat? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.EncryptedChat + } + return result + }) + } +} +public extension Api.functions.messages { + static func acceptUrlAuth(flags: Int32, peer: Api.InputPeer?, msgId: Int32?, buttonId: Int32?, url: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1322487515) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {peer!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(msgId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(buttonId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(url!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.acceptUrlAuth", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("buttonId", String(describing: buttonId)), ("url", String(describing: url))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.UrlAuthResult? in + let reader = BufferReader(buffer) + var result: Api.UrlAuthResult? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.UrlAuthResult + } + return result + }) + } +} +public extension Api.functions.messages { + static func addChatUser(chatId: Int64, userId: Api.InputUser, fwdLimit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-230206493) + serializeInt64(chatId, buffer: buffer, boxed: false) + userId.serialize(buffer, true) + serializeInt32(fwdLimit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.addChatUser", parameters: [("chatId", String(describing: chatId)), ("userId", String(describing: userId)), ("fwdLimit", String(describing: fwdLimit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func checkChatInvite(hash: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1051570619) + serializeString(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.checkChatInvite", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ChatInvite? in + let reader = BufferReader(buffer) + var result: Api.ChatInvite? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.ChatInvite + } + return result + }) + } +} +public extension Api.functions.messages { + static func checkHistoryImport(importHead: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1140726259) + serializeString(importHead, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.checkHistoryImport", parameters: [("importHead", String(describing: importHead))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.HistoryImportParsed? in + let reader = BufferReader(buffer) + var result: Api.messages.HistoryImportParsed? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.HistoryImportParsed + } + return result + }) + } +} +public extension Api.functions.messages { + static func checkHistoryImportPeer(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1573261059) + peer.serialize(buffer, true) + return (FunctionDescription(name: "messages.checkHistoryImportPeer", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.CheckedHistoryImportPeer? in + let reader = BufferReader(buffer) + var result: Api.messages.CheckedHistoryImportPeer? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.CheckedHistoryImportPeer + } + return result + }) + } +} +public extension Api.functions.messages { + static func clearAllDrafts() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2119757468) + + return (FunctionDescription(name: "messages.clearAllDrafts", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func clearRecentReactions() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1644236876) + + return (FunctionDescription(name: "messages.clearRecentReactions", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func clearRecentStickers(flags: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1986437075) + serializeInt32(flags, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.clearRecentStickers", parameters: [("flags", String(describing: flags))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func createChat(flags: Int32, users: [Api.InputUser], title: String, ttlPeriod: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(3450904) + serializeInt32(flags, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + serializeString(title, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.createChat", parameters: [("flags", String(describing: flags)), ("users", String(describing: users)), ("title", String(describing: title)), ("ttlPeriod", String(describing: ttlPeriod))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func deleteChat(chatId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1540419152) + serializeInt64(chatId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.deleteChat", parameters: [("chatId", String(describing: chatId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func deleteChatUser(flags: Int32, chatId: Int64, userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1575461717) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(chatId, buffer: buffer, boxed: false) + userId.serialize(buffer, true) + return (FunctionDescription(name: "messages.deleteChatUser", parameters: [("flags", String(describing: flags)), ("chatId", String(describing: chatId)), ("userId", String(describing: userId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func deleteExportedChatInvite(peer: Api.InputPeer, link: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-731601877) + peer.serialize(buffer, true) + serializeString(link, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.deleteExportedChatInvite", parameters: [("peer", String(describing: peer)), ("link", String(describing: link))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func deleteHistory(flags: Int32, peer: Api.InputPeer, maxId: Int32, minDate: Int32?, maxDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1332768214) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(maxId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {serializeInt32(minDate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {serializeInt32(maxDate!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.deleteHistory", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("maxId", String(describing: maxId)), ("minDate", String(describing: minDate)), ("maxDate", String(describing: maxDate))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedHistory? in + let reader = BufferReader(buffer) + var result: Api.messages.AffectedHistory? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.AffectedHistory + } + return result + }) + } +} +public extension Api.functions.messages { + static func deleteMessages(flags: Int32, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-443640366) + serializeInt32(flags, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "messages.deleteMessages", parameters: [("flags", String(describing: flags)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedMessages? in + let reader = BufferReader(buffer) + var result: Api.messages.AffectedMessages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.AffectedMessages + } + return result + }) + } +} +public extension Api.functions.messages { + static func deletePhoneCallHistory(flags: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-104078327) + serializeInt32(flags, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.deletePhoneCallHistory", parameters: [("flags", String(describing: flags))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedFoundMessages? in + let reader = BufferReader(buffer) + var result: Api.messages.AffectedFoundMessages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.AffectedFoundMessages + } + return result + }) + } +} +public extension Api.functions.messages { + static func deleteRevokedExportedChatInvites(peer: Api.InputPeer, adminId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1452833749) + peer.serialize(buffer, true) + adminId.serialize(buffer, true) + return (FunctionDescription(name: "messages.deleteRevokedExportedChatInvites", parameters: [("peer", String(describing: peer)), ("adminId", String(describing: adminId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func deleteScheduledMessages(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1504586518) + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "messages.deleteScheduledMessages", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func discardEncryption(flags: Int32, chatId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-208425312) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(chatId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.discardEncryption", parameters: [("flags", String(describing: flags)), ("chatId", String(describing: chatId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func editChatAbout(peer: Api.InputPeer, about: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-554301545) + peer.serialize(buffer, true) + serializeString(about, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.editChatAbout", parameters: [("peer", String(describing: peer)), ("about", String(describing: about))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func editChatAdmin(chatId: Int64, userId: Api.InputUser, isAdmin: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1470377534) + serializeInt64(chatId, buffer: buffer, boxed: false) + userId.serialize(buffer, true) + isAdmin.serialize(buffer, true) + return (FunctionDescription(name: "messages.editChatAdmin", parameters: [("chatId", String(describing: chatId)), ("userId", String(describing: userId)), ("isAdmin", String(describing: isAdmin))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func editChatDefaultBannedRights(peer: Api.InputPeer, bannedRights: Api.ChatBannedRights) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1517917375) + peer.serialize(buffer, true) + bannedRights.serialize(buffer, true) + return (FunctionDescription(name: "messages.editChatDefaultBannedRights", parameters: [("peer", String(describing: peer)), ("bannedRights", String(describing: bannedRights))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func editChatPhoto(chatId: Int64, photo: Api.InputChatPhoto) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(903730804) + serializeInt64(chatId, buffer: buffer, boxed: false) + photo.serialize(buffer, true) + return (FunctionDescription(name: "messages.editChatPhoto", parameters: [("chatId", String(describing: chatId)), ("photo", String(describing: photo))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func editChatTitle(chatId: Int64, title: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1937260541) + serializeInt64(chatId, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.editChatTitle", parameters: [("chatId", String(describing: chatId)), ("title", String(describing: title))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func editExportedChatInvite(flags: Int32, peer: Api.InputPeer, link: String, expireDate: Int32?, usageLimit: Int32?, requestNeeded: Api.Bool?, title: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1110823051) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeString(link, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(expireDate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(usageLimit!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {requestNeeded!.serialize(buffer, true)} + if Int(flags) & Int(1 << 4) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.editExportedChatInvite", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("link", String(describing: link)), ("expireDate", String(describing: expireDate)), ("usageLimit", String(describing: usageLimit)), ("requestNeeded", String(describing: requestNeeded)), ("title", String(describing: title))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ExportedChatInvite? in + let reader = BufferReader(buffer) + var result: Api.messages.ExportedChatInvite? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.ExportedChatInvite + } + return result + }) + } +} +public extension Api.functions.messages { + static func editInlineBotMessage(flags: Int32, id: Api.InputBotInlineMessageID, message: String?, media: Api.InputMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-2091549254) + serializeInt32(flags, buffer: buffer, boxed: false) + id.serialize(buffer, true) + if Int(flags) & Int(1 << 11) != 0 {serializeString(message!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 14) != 0 {media!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities!.count)) + for item in entities! { + item.serialize(buffer, true) + }} + return (FunctionDescription(name: "messages.editInlineBotMessage", parameters: [("flags", String(describing: flags)), ("id", String(describing: id)), ("message", String(describing: message)), ("media", String(describing: media)), ("replyMarkup", String(describing: replyMarkup)), ("entities", String(describing: entities))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func editMessage(flags: Int32, peer: Api.InputPeer, id: Int32, message: String?, media: Api.InputMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1224152952) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 11) != 0 {serializeString(message!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 14) != 0 {media!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities!.count)) + for item in entities! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 15) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.editMessage", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id)), ("message", String(describing: message)), ("media", String(describing: media)), ("replyMarkup", String(describing: replyMarkup)), ("entities", String(describing: entities)), ("scheduleDate", String(describing: scheduleDate))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func exportChatInvite(flags: Int32, peer: Api.InputPeer, expireDate: Int32?, usageLimit: Int32?, title: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1607670315) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(expireDate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(usageLimit!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.exportChatInvite", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("expireDate", String(describing: expireDate)), ("usageLimit", String(describing: usageLimit)), ("title", String(describing: title))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedChatInvite? in + let reader = BufferReader(buffer) + var result: Api.ExportedChatInvite? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite + } + return result + }) + } +} +public extension Api.functions.messages { + static func faveSticker(id: Api.InputDocument, unfave: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1174420133) + id.serialize(buffer, true) + unfave.serialize(buffer, true) + return (FunctionDescription(name: "messages.faveSticker", parameters: [("id", String(describing: id)), ("unfave", String(describing: unfave))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func forwardMessages(flags: Int32, fromPeer: Api.InputPeer, id: [Int32], randomId: [Int64], toPeer: Api.InputPeer, topMsgId: Int32?, scheduleDate: Int32?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-966673468) + serializeInt32(flags, buffer: buffer, boxed: false) + fromPeer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(randomId.count)) + for item in randomId { + serializeInt64(item, buffer: buffer, boxed: false) + } + toPeer.serialize(buffer, true) + if Int(flags) & Int(1 << 9) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)} + return (FunctionDescription(name: "messages.forwardMessages", parameters: [("flags", String(describing: flags)), ("fromPeer", String(describing: fromPeer)), ("id", String(describing: id)), ("randomId", String(describing: randomId)), ("toPeer", String(describing: toPeer)), ("topMsgId", String(describing: topMsgId)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func getAdminsWithInvites(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(958457583) + peer.serialize(buffer, true) + return (FunctionDescription(name: "messages.getAdminsWithInvites", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ChatAdminsWithInvites? in + let reader = BufferReader(buffer) + var result: Api.messages.ChatAdminsWithInvites? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.ChatAdminsWithInvites + } + return result + }) + } +} +public extension Api.functions.messages { + static func getAllDrafts() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1782549861) + + return (FunctionDescription(name: "messages.getAllDrafts", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func getAllStickers(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1197432408) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getAllStickers", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AllStickers? in + let reader = BufferReader(buffer) + var result: Api.messages.AllStickers? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.AllStickers + } + return result + }) + } +} +public extension Api.functions.messages { + static func getArchivedStickers(flags: Int32, offsetId: Int64, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1475442322) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(offsetId, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getArchivedStickers", parameters: [("flags", String(describing: flags)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ArchivedStickers? in + let reader = BufferReader(buffer) + var result: Api.messages.ArchivedStickers? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.ArchivedStickers + } + return result + }) + } +} +public extension Api.functions.messages { + static func getAttachMenuBot(bot: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1998676370) + bot.serialize(buffer, true) + return (FunctionDescription(name: "messages.getAttachMenuBot", parameters: [("bot", String(describing: bot))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.AttachMenuBotsBot? in + let reader = BufferReader(buffer) + var result: Api.AttachMenuBotsBot? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.AttachMenuBotsBot + } + return result + }) + } +} +public extension Api.functions.messages { + static func getAttachMenuBots(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(385663691) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getAttachMenuBots", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.AttachMenuBots? in + let reader = BufferReader(buffer) + var result: Api.AttachMenuBots? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.AttachMenuBots + } + return result + }) + } +} +public extension Api.functions.messages { + static func getAttachedStickers(media: Api.InputStickeredMedia) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.StickerSetCovered]>) { + let buffer = Buffer() + buffer.appendInt32(-866424884) + media.serialize(buffer, true) + return (FunctionDescription(name: "messages.getAttachedStickers", parameters: [("media", String(describing: media))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.StickerSetCovered]? in + let reader = BufferReader(buffer) + var result: [Api.StickerSetCovered]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self) + } + return result + }) + } +} +public extension Api.functions.messages { + static func getAvailableReactions(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(417243308) + serializeInt32(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getAvailableReactions", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AvailableReactions? in + let reader = BufferReader(buffer) + var result: Api.messages.AvailableReactions? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.AvailableReactions + } + return result + }) + } +} +public extension Api.functions.messages { + static func getBotApp(app: Api.InputBotApp, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(889046467) + app.serialize(buffer, true) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getBotApp", parameters: [("app", String(describing: app)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.BotApp? in + let reader = BufferReader(buffer) + var result: Api.messages.BotApp? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.BotApp + } + return result + }) + } +} +public extension Api.functions.messages { + static func getBotCallbackAnswer(flags: Int32, peer: Api.InputPeer, msgId: Int32, data: Buffer?, password: Api.InputCheckPasswordSRP?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1824339449) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeBytes(data!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {password!.serialize(buffer, true)} + return (FunctionDescription(name: "messages.getBotCallbackAnswer", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("data", String(describing: data)), ("password", String(describing: password))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.BotCallbackAnswer? in + let reader = BufferReader(buffer) + var result: Api.messages.BotCallbackAnswer? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.BotCallbackAnswer + } + return result + }) + } +} +public extension Api.functions.messages { + static func getChatInviteImporters(flags: Int32, peer: Api.InputPeer, link: String?, q: String?, offsetDate: Int32, offsetUser: Api.InputUser, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-553329330) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 1) != 0 {serializeString(link!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(q!, buffer: buffer, boxed: false)} + serializeInt32(offsetDate, buffer: buffer, boxed: false) + offsetUser.serialize(buffer, true) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getChatInviteImporters", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("link", String(describing: link)), ("q", String(describing: q)), ("offsetDate", String(describing: offsetDate)), ("offsetUser", String(describing: offsetUser)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ChatInviteImporters? in + let reader = BufferReader(buffer) + var result: Api.messages.ChatInviteImporters? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.ChatInviteImporters + } + return result + }) + } +} +public extension Api.functions.messages { + static func getChats(id: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1240027791) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt64(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "messages.getChats", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Chats? in + let reader = BufferReader(buffer) + var result: Api.messages.Chats? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Chats + } + return result + }) + } +} +public extension Api.functions.messages { + static func getCommonChats(userId: Api.InputUser, maxId: Int64, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-468934396) + userId.serialize(buffer, true) + serializeInt64(maxId, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getCommonChats", parameters: [("userId", String(describing: userId)), ("maxId", String(describing: maxId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Chats? in + let reader = BufferReader(buffer) + var result: Api.messages.Chats? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Chats + } + return result + }) + } +} +public extension Api.functions.messages { + static func getCustomEmojiDocuments(documentId: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.Document]>) { + let buffer = Buffer() + buffer.appendInt32(-643100844) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(documentId.count)) + for item in documentId { + serializeInt64(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "messages.getCustomEmojiDocuments", parameters: [("documentId", String(describing: documentId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.Document]? in + let reader = BufferReader(buffer) + var result: [Api.Document]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + } + return result + }) + } +} +public extension Api.functions.messages { + static func getDefaultHistoryTTL() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1703637384) + + return (FunctionDescription(name: "messages.getDefaultHistoryTTL", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.DefaultHistoryTTL? in + let reader = BufferReader(buffer) + var result: Api.DefaultHistoryTTL? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.DefaultHistoryTTL + } + return result + }) + } +} +public extension Api.functions.messages { + static func getDhConfig(version: Int32, randomLength: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(651135312) + serializeInt32(version, buffer: buffer, boxed: false) + serializeInt32(randomLength, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getDhConfig", parameters: [("version", String(describing: version)), ("randomLength", String(describing: randomLength))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.DhConfig? in + let reader = BufferReader(buffer) + var result: Api.messages.DhConfig? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.DhConfig + } + return result + }) + } +} +public extension Api.functions.messages { + static func getDialogFilters() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.DialogFilter]>) { + let buffer = Buffer() + buffer.appendInt32(-241247891) + + return (FunctionDescription(name: "messages.getDialogFilters", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.DialogFilter]? in + let reader = BufferReader(buffer) + var result: [Api.DialogFilter]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.DialogFilter.self) + } + return result + }) + } +} +public extension Api.functions.messages { + static func getDialogUnreadMarks() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.DialogPeer]>) { + let buffer = Buffer() + buffer.appendInt32(585256482) + + return (FunctionDescription(name: "messages.getDialogUnreadMarks", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.DialogPeer]? in + let reader = BufferReader(buffer) + var result: [Api.DialogPeer]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.DialogPeer.self) + } + return result + }) + } +} +public extension Api.functions.messages { + static func getDialogs(flags: Int32, folderId: Int32?, offsetDate: Int32, offsetId: Int32, offsetPeer: Api.InputPeer, limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1594569905) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} + serializeInt32(offsetDate, buffer: buffer, boxed: false) + serializeInt32(offsetId, buffer: buffer, boxed: false) + offsetPeer.serialize(buffer, true) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getDialogs", parameters: [("flags", String(describing: flags)), ("folderId", String(describing: folderId)), ("offsetDate", String(describing: offsetDate)), ("offsetId", String(describing: offsetId)), ("offsetPeer", String(describing: offsetPeer)), ("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Dialogs? in + let reader = BufferReader(buffer) + var result: Api.messages.Dialogs? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Dialogs + } + return result + }) + } +} +public extension Api.functions.messages { + static func getDiscussionMessage(peer: Api.InputPeer, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1147761405) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getDiscussionMessage", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.DiscussionMessage? in + let reader = BufferReader(buffer) + var result: Api.messages.DiscussionMessage? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.DiscussionMessage + } + return result + }) + } +} +public extension Api.functions.messages { + static func getDocumentByHash(sha256: Buffer, size: Int64, mimeType: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1309538785) + serializeBytes(sha256, buffer: buffer, boxed: false) + serializeInt64(size, buffer: buffer, boxed: false) + serializeString(mimeType, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getDocumentByHash", parameters: [("sha256", String(describing: sha256)), ("size", String(describing: size)), ("mimeType", String(describing: mimeType))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Document? in + let reader = BufferReader(buffer) + var result: Api.Document? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Document + } + return result + }) + } +} +public extension Api.functions.messages { + static func getEmojiGroups(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1955122779) + serializeInt32(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getEmojiGroups", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.EmojiGroups? in + let reader = BufferReader(buffer) + var result: Api.messages.EmojiGroups? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.EmojiGroups + } + return result + }) + } +} +public extension Api.functions.messages { + static func getEmojiKeywords(langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(899735650) + serializeString(langCode, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getEmojiKeywords", parameters: [("langCode", String(describing: langCode))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EmojiKeywordsDifference? in + let reader = BufferReader(buffer) + var result: Api.EmojiKeywordsDifference? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.EmojiKeywordsDifference + } + return result + }) + } +} +public extension Api.functions.messages { + static func getEmojiKeywordsDifference(langCode: String, fromVersion: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(352892591) + serializeString(langCode, buffer: buffer, boxed: false) + serializeInt32(fromVersion, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getEmojiKeywordsDifference", parameters: [("langCode", String(describing: langCode)), ("fromVersion", String(describing: fromVersion))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EmojiKeywordsDifference? in + let reader = BufferReader(buffer) + var result: Api.EmojiKeywordsDifference? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.EmojiKeywordsDifference + } + return result + }) + } +} +public extension Api.functions.messages { + static func getEmojiKeywordsLanguages(langCodes: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.EmojiLanguage]>) { + let buffer = Buffer() + buffer.appendInt32(1318675378) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(langCodes.count)) + for item in langCodes { + serializeString(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "messages.getEmojiKeywordsLanguages", parameters: [("langCodes", String(describing: langCodes))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.EmojiLanguage]? in + let reader = BufferReader(buffer) + var result: [Api.EmojiLanguage]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.EmojiLanguage.self) + } + return result + }) + } +} +public extension Api.functions.messages { + static func getEmojiProfilePhotoGroups(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(564480243) + serializeInt32(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getEmojiProfilePhotoGroups", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.EmojiGroups? in + let reader = BufferReader(buffer) + var result: Api.messages.EmojiGroups? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.EmojiGroups + } + return result + }) + } +} +public extension Api.functions.messages { + static func getEmojiStatusGroups(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(785209037) + serializeInt32(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getEmojiStatusGroups", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.EmojiGroups? in + let reader = BufferReader(buffer) + var result: Api.messages.EmojiGroups? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.EmojiGroups + } + return result + }) + } +} +public extension Api.functions.messages { + static func getEmojiStickers(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-67329649) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getEmojiStickers", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AllStickers? in + let reader = BufferReader(buffer) + var result: Api.messages.AllStickers? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.AllStickers + } + return result + }) + } +} +public extension Api.functions.messages { + static func getEmojiURL(langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-709817306) + serializeString(langCode, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getEmojiURL", parameters: [("langCode", String(describing: langCode))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EmojiURL? in + let reader = BufferReader(buffer) + var result: Api.EmojiURL? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.EmojiURL + } + return result + }) + } +} +public extension Api.functions.messages { + static func getExportedChatInvite(peer: Api.InputPeer, link: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1937010524) + peer.serialize(buffer, true) + serializeString(link, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getExportedChatInvite", parameters: [("peer", String(describing: peer)), ("link", String(describing: link))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ExportedChatInvite? in + let reader = BufferReader(buffer) + var result: Api.messages.ExportedChatInvite? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.ExportedChatInvite + } + return result + }) + } +} +public extension Api.functions.messages { + static func getExportedChatInvites(flags: Int32, peer: Api.InputPeer, adminId: Api.InputUser, offsetDate: Int32?, offsetLink: String?, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1565154314) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + adminId.serialize(buffer, true) + if Int(flags) & Int(1 << 2) != 0 {serializeInt32(offsetDate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(offsetLink!, buffer: buffer, boxed: false)} + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getExportedChatInvites", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("adminId", String(describing: adminId)), ("offsetDate", String(describing: offsetDate)), ("offsetLink", String(describing: offsetLink)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ExportedChatInvites? in + let reader = BufferReader(buffer) + var result: Api.messages.ExportedChatInvites? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.ExportedChatInvites + } + return result + }) + } +} +public extension Api.functions.messages { + static func getExtendedMedia(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-2064119788) + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "messages.getExtendedMedia", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func getFavedStickers(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(82946729) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getFavedStickers", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.FavedStickers? in + let reader = BufferReader(buffer) + var result: Api.messages.FavedStickers? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.FavedStickers + } + return result + }) + } +} +public extension Api.functions.messages { + static func getFeaturedEmojiStickers(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(248473398) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getFeaturedEmojiStickers", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.FeaturedStickers? in + let reader = BufferReader(buffer) + var result: Api.messages.FeaturedStickers? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.FeaturedStickers + } + return result + }) + } +} +public extension Api.functions.messages { + static func getFeaturedStickers(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1685588756) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getFeaturedStickers", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.FeaturedStickers? in + let reader = BufferReader(buffer) + var result: Api.messages.FeaturedStickers? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.FeaturedStickers + } + return result + }) + } +} +public extension Api.functions.messages { + static func getFullChat(chatId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1364194508) + serializeInt64(chatId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getFullChat", parameters: [("chatId", String(describing: chatId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ChatFull? in + let reader = BufferReader(buffer) + var result: Api.messages.ChatFull? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.ChatFull + } + return result + }) + } +} +public extension Api.functions.messages { + static func getGameHighScores(peer: Api.InputPeer, id: Int32, userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-400399203) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + userId.serialize(buffer, true) + return (FunctionDescription(name: "messages.getGameHighScores", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("userId", String(describing: userId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.HighScores? in + let reader = BufferReader(buffer) + var result: Api.messages.HighScores? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.HighScores + } + return result + }) + } +} +public extension Api.functions.messages { + static func getHistory(peer: Api.InputPeer, offsetId: Int32, offsetDate: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1143203525) + peer.serialize(buffer, true) + serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeInt32(offsetDate, buffer: buffer, boxed: false) + serializeInt32(addOffset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt32(maxId, buffer: buffer, boxed: false) + serializeInt32(minId, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getHistory", parameters: [("peer", String(describing: peer)), ("offsetId", String(describing: offsetId)), ("offsetDate", String(describing: offsetDate)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + let reader = BufferReader(buffer) + var result: Api.messages.Messages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Messages + } + return result + }) + } +} +public extension Api.functions.messages { + static func getInlineBotResults(flags: Int32, bot: Api.InputUser, peer: Api.InputPeer, geoPoint: Api.InputGeoPoint?, query: String, offset: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1364105629) + serializeInt32(flags, buffer: buffer, boxed: false) + bot.serialize(buffer, true) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {geoPoint!.serialize(buffer, true)} + serializeString(query, buffer: buffer, boxed: false) + serializeString(offset, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getInlineBotResults", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("peer", String(describing: peer)), ("geoPoint", String(describing: geoPoint)), ("query", String(describing: query)), ("offset", String(describing: offset))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.BotResults? in + let reader = BufferReader(buffer) + var result: Api.messages.BotResults? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.BotResults + } + return result + }) + } +} +public extension Api.functions.messages { + static func getInlineGameHighScores(id: Api.InputBotInlineMessageID, userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(258170395) + id.serialize(buffer, true) + userId.serialize(buffer, true) + return (FunctionDescription(name: "messages.getInlineGameHighScores", parameters: [("id", String(describing: id)), ("userId", String(describing: userId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.HighScores? in + let reader = BufferReader(buffer) + var result: Api.messages.HighScores? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.HighScores + } + return result + }) + } +} +public extension Api.functions.messages { + static func getMaskStickers(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1678738104) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getMaskStickers", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AllStickers? in + let reader = BufferReader(buffer) + var result: Api.messages.AllStickers? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.AllStickers + } + return result + }) + } +} +public extension Api.functions.messages { + static func getMessageEditData(peer: Api.InputPeer, id: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-39416522) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getMessageEditData", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.MessageEditData? in + let reader = BufferReader(buffer) + var result: Api.messages.MessageEditData? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.MessageEditData + } + return result + }) + } +} +public extension Api.functions.messages { + static func getMessageReactionsList(flags: Int32, peer: Api.InputPeer, id: Int32, reaction: Api.Reaction?, offset: String?, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1176190792) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {reaction!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(offset!, buffer: buffer, boxed: false)} + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getMessageReactionsList", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id)), ("reaction", String(describing: reaction)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.MessageReactionsList? in + let reader = BufferReader(buffer) + var result: Api.messages.MessageReactionsList? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.MessageReactionsList + } + return result + }) + } +} +public extension Api.functions.messages { + static func getMessageReadParticipants(peer: Api.InputPeer, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.ReadParticipantDate]>) { + let buffer = Buffer() + buffer.appendInt32(834782287) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getMessageReadParticipants", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.ReadParticipantDate]? in + let reader = BufferReader(buffer) + var result: [Api.ReadParticipantDate]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.ReadParticipantDate.self) + } + return result + }) + } +} +public extension Api.functions.messages { + static func getMessages(id: [Api.InputMessage]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1673946374) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "messages.getMessages", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + let reader = BufferReader(buffer) + var result: Api.messages.Messages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Messages + } + return result + }) + } +} +public extension Api.functions.messages { + static func getMessagesReactions(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1950707482) + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "messages.getMessagesReactions", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func getMessagesViews(peer: Api.InputPeer, id: [Int32], increment: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1468322785) + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + increment.serialize(buffer, true) + return (FunctionDescription(name: "messages.getMessagesViews", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("increment", String(describing: increment))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.MessageViews? in + let reader = BufferReader(buffer) + var result: Api.messages.MessageViews? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.MessageViews + } + return result + }) + } +} +public extension Api.functions.messages { + static func getOldFeaturedStickers(offset: Int32, limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2127598753) + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getOldFeaturedStickers", parameters: [("offset", String(describing: offset)), ("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.FeaturedStickers? in + let reader = BufferReader(buffer) + var result: Api.messages.FeaturedStickers? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.FeaturedStickers + } + return result + }) + } +} +public extension Api.functions.messages { + static func getOnlines(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1848369232) + peer.serialize(buffer, true) + return (FunctionDescription(name: "messages.getOnlines", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ChatOnlines? in + let reader = BufferReader(buffer) + var result: Api.ChatOnlines? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.ChatOnlines + } + return result + }) + } +} +public extension Api.functions.messages { + static func getPeerDialogs(peers: [Api.InputDialogPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-462373635) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peers.count)) + for item in peers { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "messages.getPeerDialogs", parameters: [("peers", String(describing: peers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.PeerDialogs? in + let reader = BufferReader(buffer) + var result: Api.messages.PeerDialogs? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.PeerDialogs + } + return result + }) + } +} +public extension Api.functions.messages { + static func getPeerSettings(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-270948702) + peer.serialize(buffer, true) + return (FunctionDescription(name: "messages.getPeerSettings", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.PeerSettings? in + let reader = BufferReader(buffer) + var result: Api.messages.PeerSettings? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.PeerSettings + } + return result + }) + } +} +public extension Api.functions.messages { + static func getPinnedDialogs(folderId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-692498958) + serializeInt32(folderId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getPinnedDialogs", parameters: [("folderId", String(describing: folderId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.PeerDialogs? in + let reader = BufferReader(buffer) + var result: Api.messages.PeerDialogs? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.PeerDialogs + } + return result + }) + } +} +public extension Api.functions.messages { + static func getPollResults(peer: Api.InputPeer, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1941660731) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getPollResults", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func getPollVotes(flags: Int32, peer: Api.InputPeer, id: Int32, option: Buffer?, offset: String?, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1200736242) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeBytes(option!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(offset!, buffer: buffer, boxed: false)} + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getPollVotes", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id)), ("option", String(describing: option)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.VotesList? in + let reader = BufferReader(buffer) + var result: Api.messages.VotesList? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.VotesList + } + return result + }) + } +} +public extension Api.functions.messages { + static func getRecentLocations(peer: Api.InputPeer, limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1881817312) + peer.serialize(buffer, true) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getRecentLocations", parameters: [("peer", String(describing: peer)), ("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + let reader = BufferReader(buffer) + var result: Api.messages.Messages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Messages + } + return result + }) + } +} +public extension Api.functions.messages { + static func getRecentReactions(limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(960896434) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getRecentReactions", parameters: [("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Reactions? in + let reader = BufferReader(buffer) + var result: Api.messages.Reactions? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Reactions + } + return result + }) + } +} +public extension Api.functions.messages { + static func getRecentStickers(flags: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1649852357) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getRecentStickers", parameters: [("flags", String(describing: flags)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.RecentStickers? in + let reader = BufferReader(buffer) + var result: Api.messages.RecentStickers? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.RecentStickers + } + return result + }) + } +} +public extension Api.functions.messages { + static func getReplies(peer: Api.InputPeer, msgId: Int32, offsetId: Int32, offsetDate: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(584962828) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeInt32(offsetDate, buffer: buffer, boxed: false) + serializeInt32(addOffset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt32(maxId, buffer: buffer, boxed: false) + serializeInt32(minId, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getReplies", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("offsetId", String(describing: offsetId)), ("offsetDate", String(describing: offsetDate)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + let reader = BufferReader(buffer) + var result: Api.messages.Messages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Messages + } + return result + }) + } +} +public extension Api.functions.messages { + static func getSavedGifs(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1559270965) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getSavedGifs", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SavedGifs? in + let reader = BufferReader(buffer) + var result: Api.messages.SavedGifs? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.SavedGifs + } + return result + }) + } +} +public extension Api.functions.messages { + static func getScheduledHistory(peer: Api.InputPeer, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-183077365) + peer.serialize(buffer, true) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getScheduledHistory", parameters: [("peer", String(describing: peer)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + let reader = BufferReader(buffer) + var result: Api.messages.Messages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Messages + } + return result + }) + } +} +public extension Api.functions.messages { + static func getScheduledMessages(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1111817116) + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "messages.getScheduledMessages", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + let reader = BufferReader(buffer) + var result: Api.messages.Messages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Messages + } + return result + }) + } +} +public extension Api.functions.messages { + static func getSearchCounters(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?, filters: [Api.MessagesFilter]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.messages.SearchCounter]>) { + let buffer = Buffer() + buffer.appendInt32(11435201) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(filters.count)) + for item in filters { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "messages.getSearchCounters", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId)), ("filters", String(describing: filters))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.messages.SearchCounter]? in + let reader = BufferReader(buffer) + var result: [Api.messages.SearchCounter]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.messages.SearchCounter.self) + } + return result + }) + } +} +public extension Api.functions.messages { + static func getSearchResultsCalendar(peer: Api.InputPeer, filter: Api.MessagesFilter, offsetId: Int32, offsetDate: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1240514025) + peer.serialize(buffer, true) + filter.serialize(buffer, true) + serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeInt32(offsetDate, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getSearchResultsCalendar", parameters: [("peer", String(describing: peer)), ("filter", String(describing: filter)), ("offsetId", String(describing: offsetId)), ("offsetDate", String(describing: offsetDate))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SearchResultsCalendar? in + let reader = BufferReader(buffer) + var result: Api.messages.SearchResultsCalendar? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.SearchResultsCalendar + } + return result + }) + } +} +public extension Api.functions.messages { + static func getSearchResultsPositions(peer: Api.InputPeer, filter: Api.MessagesFilter, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1855292323) + peer.serialize(buffer, true) + filter.serialize(buffer, true) + serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getSearchResultsPositions", parameters: [("peer", String(describing: peer)), ("filter", String(describing: filter)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SearchResultsPositions? in + let reader = BufferReader(buffer) + var result: Api.messages.SearchResultsPositions? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.SearchResultsPositions + } + return result + }) + } +} +public extension Api.functions.messages { + static func getSplitRanges() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.MessageRange]>) { + let buffer = Buffer() + buffer.appendInt32(486505992) + + return (FunctionDescription(name: "messages.getSplitRanges", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.MessageRange]? in + let reader = BufferReader(buffer) + var result: [Api.MessageRange]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageRange.self) + } + return result + }) + } +} +public extension Api.functions.messages { + static func getStickerSet(stickerset: Api.InputStickerSet, hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-928977804) + stickerset.serialize(buffer, true) + serializeInt32(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getStickerSet", parameters: [("stickerset", String(describing: stickerset)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in + let reader = BufferReader(buffer) + var result: Api.messages.StickerSet? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.StickerSet + } + return result + }) + } +} +public extension Api.functions.messages { + static func getStickers(emoticon: String, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-710552671) + serializeString(emoticon, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getStickers", parameters: [("emoticon", String(describing: emoticon)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Stickers? in + let reader = BufferReader(buffer) + var result: Api.messages.Stickers? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Stickers + } + return result + }) + } +} +public extension Api.functions.messages { + static func getSuggestedDialogFilters() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.DialogFilterSuggested]>) { + let buffer = Buffer() + buffer.appendInt32(-1566780372) + + return (FunctionDescription(name: "messages.getSuggestedDialogFilters", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.DialogFilterSuggested]? in + let reader = BufferReader(buffer) + var result: [Api.DialogFilterSuggested]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.DialogFilterSuggested.self) + } + return result + }) + } +} +public extension Api.functions.messages { + static func getTopReactions(limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1149164102) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getTopReactions", parameters: [("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Reactions? in + let reader = BufferReader(buffer) + var result: Api.messages.Reactions? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Reactions + } + return result + }) + } +} +public extension Api.functions.messages { + static func getUnreadMentions(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-251140208) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} + serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeInt32(addOffset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt32(maxId, buffer: buffer, boxed: false) + serializeInt32(minId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getUnreadMentions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId)), ("offsetId", String(describing: offsetId)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + let reader = BufferReader(buffer) + var result: Api.messages.Messages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Messages + } + return result + }) + } +} +public extension Api.functions.messages { + static func getUnreadReactions(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(841173339) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} + serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeInt32(addOffset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt32(maxId, buffer: buffer, boxed: false) + serializeInt32(minId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getUnreadReactions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId)), ("offsetId", String(describing: offsetId)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + let reader = BufferReader(buffer) + var result: Api.messages.Messages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Messages + } + return result + }) + } +} +public extension Api.functions.messages { + static func getWebPage(url: String, hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1919511901) + serializeString(url, buffer: buffer, boxed: false) + serializeInt32(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getWebPage", parameters: [("url", String(describing: url)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.WebPage? in + let reader = BufferReader(buffer) + var result: Api.messages.WebPage? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.WebPage + } + return result + }) + } +} +public extension Api.functions.messages { + static func getWebPagePreview(flags: Int32, message: String, entities: [Api.MessageEntity]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1956073268) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(message, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities!.count)) + for item in entities! { + item.serialize(buffer, true) + }} + return (FunctionDescription(name: "messages.getWebPagePreview", parameters: [("flags", String(describing: flags)), ("message", String(describing: message)), ("entities", String(describing: entities))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.MessageMedia? in + let reader = BufferReader(buffer) + var result: Api.MessageMedia? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.MessageMedia + } + return result + }) + } +} +public extension Api.functions.messages { + static func hideAllChatJoinRequests(flags: Int32, peer: Api.InputPeer, link: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-528091926) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 1) != 0 {serializeString(link!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.hideAllChatJoinRequests", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("link", String(describing: link))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func hideChatJoinRequest(flags: Int32, peer: Api.InputPeer, userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2145904661) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + userId.serialize(buffer, true) + return (FunctionDescription(name: "messages.hideChatJoinRequest", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("userId", String(describing: userId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func hidePeerSettingsBar(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1336717624) + peer.serialize(buffer, true) + return (FunctionDescription(name: "messages.hidePeerSettingsBar", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func importChatInvite(hash: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1817183516) + serializeString(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.importChatInvite", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func initHistoryImport(peer: Api.InputPeer, file: Api.InputFile, mediaCount: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(873008187) + peer.serialize(buffer, true) + file.serialize(buffer, true) + serializeInt32(mediaCount, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.initHistoryImport", parameters: [("peer", String(describing: peer)), ("file", String(describing: file)), ("mediaCount", String(describing: mediaCount))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.HistoryImport? in + let reader = BufferReader(buffer) + var result: Api.messages.HistoryImport? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.HistoryImport + } + return result + }) + } +} +public extension Api.functions.messages { + static func installStickerSet(stickerset: Api.InputStickerSet, archived: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-946871200) + stickerset.serialize(buffer, true) + archived.serialize(buffer, true) + return (FunctionDescription(name: "messages.installStickerSet", parameters: [("stickerset", String(describing: stickerset)), ("archived", String(describing: archived))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSetInstallResult? in + let reader = BufferReader(buffer) + var result: Api.messages.StickerSetInstallResult? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.StickerSetInstallResult + } + return result + }) + } +} +public extension Api.functions.messages { + static func markDialogUnread(flags: Int32, peer: Api.InputDialogPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1031349873) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + return (FunctionDescription(name: "messages.markDialogUnread", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func migrateChat(chatId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1568189671) + serializeInt64(chatId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.migrateChat", parameters: [("chatId", String(describing: chatId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func prolongWebView(flags: Int32, peer: Api.InputPeer, bot: Api.InputUser, queryId: Int64, replyTo: Api.InputReplyTo?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1328014717) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + bot.serialize(buffer, true) + serializeInt64(queryId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {replyTo!.serialize(buffer, true)} + if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)} + return (FunctionDescription(name: "messages.prolongWebView", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("bot", String(describing: bot)), ("queryId", String(describing: queryId)), ("replyTo", String(describing: replyTo)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func rateTranscribedAudio(peer: Api.InputPeer, msgId: Int32, transcriptionId: Int64, good: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2132608815) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + serializeInt64(transcriptionId, buffer: buffer, boxed: false) + good.serialize(buffer, true) + return (FunctionDescription(name: "messages.rateTranscribedAudio", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("transcriptionId", String(describing: transcriptionId)), ("good", String(describing: good))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func readDiscussion(peer: Api.InputPeer, msgId: Int32, readMaxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-147740172) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + serializeInt32(readMaxId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.readDiscussion", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("readMaxId", String(describing: readMaxId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func readEncryptedHistory(peer: Api.InputEncryptedChat, maxDate: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2135648522) + peer.serialize(buffer, true) + serializeInt32(maxDate, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.readEncryptedHistory", parameters: [("peer", String(describing: peer)), ("maxDate", String(describing: maxDate))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func readFeaturedStickers(id: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1527873830) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt64(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "messages.readFeaturedStickers", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func readHistory(peer: Api.InputPeer, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(238054714) + peer.serialize(buffer, true) + serializeInt32(maxId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.readHistory", parameters: [("peer", String(describing: peer)), ("maxId", String(describing: maxId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedMessages? in + let reader = BufferReader(buffer) + var result: Api.messages.AffectedMessages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.AffectedMessages + } + return result + }) + } +} +public extension Api.functions.messages { + static func readMentions(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(921026381) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.readMentions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedHistory? in + let reader = BufferReader(buffer) + var result: Api.messages.AffectedHistory? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.AffectedHistory + } + return result + }) + } +} +public extension Api.functions.messages { + static func readMessageContents(id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(916930423) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "messages.readMessageContents", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedMessages? in + let reader = BufferReader(buffer) + var result: Api.messages.AffectedMessages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.AffectedMessages + } + return result + }) + } +} +public extension Api.functions.messages { + static func readReactions(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1420459918) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.readReactions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedHistory? in + let reader = BufferReader(buffer) + var result: Api.messages.AffectedHistory? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.AffectedHistory + } + return result + }) + } +} +public extension Api.functions.messages { + static func receivedMessages(maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.ReceivedNotifyMessage]>) { + let buffer = Buffer() + buffer.appendInt32(94983360) + serializeInt32(maxId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.receivedMessages", parameters: [("maxId", String(describing: maxId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.ReceivedNotifyMessage]? in + let reader = BufferReader(buffer) + var result: [Api.ReceivedNotifyMessage]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.ReceivedNotifyMessage.self) + } + return result + }) + } +} +public extension Api.functions.messages { + static func receivedQueue(maxQts: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int64]>) { + let buffer = Buffer() + buffer.appendInt32(1436924774) + serializeInt32(maxQts, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.receivedQueue", parameters: [("maxQts", String(describing: maxQts))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int64]? in + let reader = BufferReader(buffer) + var result: [Int64]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } + return result + }) + } +} +public extension Api.functions.messages { + static func reorderPinnedDialogs(flags: Int32, folderId: Int32, order: [Api.InputDialogPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(991616823) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(folderId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order.count)) + for item in order { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "messages.reorderPinnedDialogs", parameters: [("flags", String(describing: flags)), ("folderId", String(describing: folderId)), ("order", String(describing: order))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func reorderStickerSets(flags: Int32, order: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2016638777) + serializeInt32(flags, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order.count)) + for item in order { + serializeInt64(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "messages.reorderStickerSets", parameters: [("flags", String(describing: flags)), ("order", String(describing: order))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func report(peer: Api.InputPeer, id: [Int32], reason: Api.ReportReason, message: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1991005362) + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + reason.serialize(buffer, true) + serializeString(message, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.report", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("reason", String(describing: reason)), ("message", String(describing: message))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func reportEncryptedSpam(peer: Api.InputEncryptedChat) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1259113487) + peer.serialize(buffer, true) + return (FunctionDescription(name: "messages.reportEncryptedSpam", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func reportReaction(peer: Api.InputPeer, id: Int32, reactionPeer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1063567478) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + reactionPeer.serialize(buffer, true) + return (FunctionDescription(name: "messages.reportReaction", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("reactionPeer", String(describing: reactionPeer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func reportSpam(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-820669733) + peer.serialize(buffer, true) + return (FunctionDescription(name: "messages.reportSpam", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func requestAppWebView(flags: Int32, peer: Api.InputPeer, app: Api.InputBotApp, startParam: String?, themeParams: Api.DataJSON?, platform: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1940243652) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + app.serialize(buffer, true) + if Int(flags) & Int(1 << 1) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {themeParams!.serialize(buffer, true)} + serializeString(platform, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.requestAppWebView", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("app", String(describing: app)), ("startParam", String(describing: startParam)), ("themeParams", String(describing: themeParams)), ("platform", String(describing: platform))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.AppWebViewResult? in + let reader = BufferReader(buffer) + var result: Api.AppWebViewResult? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.AppWebViewResult + } + return result + }) + } +} +public extension Api.functions.messages { + static func requestEncryption(userId: Api.InputUser, randomId: Int32, gA: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-162681021) + userId.serialize(buffer, true) + serializeInt32(randomId, buffer: buffer, boxed: false) + serializeBytes(gA, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.requestEncryption", parameters: [("userId", String(describing: userId)), ("randomId", String(describing: randomId)), ("gA", String(describing: gA))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EncryptedChat? in + let reader = BufferReader(buffer) + var result: Api.EncryptedChat? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.EncryptedChat + } + return result + }) + } +} +public extension Api.functions.messages { + static func requestSimpleWebView(flags: Int32, bot: Api.InputUser, url: String?, startParam: String?, themeParams: Api.DataJSON?, platform: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(440815626) + serializeInt32(flags, buffer: buffer, boxed: false) + bot.serialize(buffer, true) + if Int(flags) & Int(1 << 3) != 0 {serializeString(url!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {themeParams!.serialize(buffer, true)} + serializeString(platform, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.requestSimpleWebView", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("url", String(describing: url)), ("startParam", String(describing: startParam)), ("themeParams", String(describing: themeParams)), ("platform", String(describing: platform))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.SimpleWebViewResult? in + let reader = BufferReader(buffer) + var result: Api.SimpleWebViewResult? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.SimpleWebViewResult + } + return result + }) + } +} +public extension Api.functions.messages { + static func requestUrlAuth(flags: Int32, peer: Api.InputPeer?, msgId: Int32?, buttonId: Int32?, url: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(428848198) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {peer!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(msgId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(buttonId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(url!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.requestUrlAuth", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("buttonId", String(describing: buttonId)), ("url", String(describing: url))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.UrlAuthResult? in + let reader = BufferReader(buffer) + var result: Api.UrlAuthResult? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.UrlAuthResult + } + return result + }) + } +} +public extension Api.functions.messages { + static func requestWebView(flags: Int32, peer: Api.InputPeer, bot: Api.InputUser, url: String?, startParam: String?, themeParams: Api.DataJSON?, platform: String, replyTo: Api.InputReplyTo?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(647873217) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + bot.serialize(buffer, true) + if Int(flags) & Int(1 << 1) != 0 {serializeString(url!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {themeParams!.serialize(buffer, true)} + serializeString(platform, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {replyTo!.serialize(buffer, true)} + if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)} + return (FunctionDescription(name: "messages.requestWebView", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("bot", String(describing: bot)), ("url", String(describing: url)), ("startParam", String(describing: startParam)), ("themeParams", String(describing: themeParams)), ("platform", String(describing: platform)), ("replyTo", String(describing: replyTo)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.WebViewResult? in + let reader = BufferReader(buffer) + var result: Api.WebViewResult? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.WebViewResult + } + return result + }) + } +} +public extension Api.functions.messages { + static func saveDefaultSendAs(peer: Api.InputPeer, sendAs: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-855777386) + peer.serialize(buffer, true) + sendAs.serialize(buffer, true) + return (FunctionDescription(name: "messages.saveDefaultSendAs", parameters: [("peer", String(describing: peer)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func saveDraft(flags: Int32, replyTo: Api.InputReplyTo?, peer: Api.InputPeer, message: String, entities: [Api.MessageEntity]?, media: Api.InputMedia?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2146678790) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 4) != 0 {replyTo!.serialize(buffer, true)} + peer.serialize(buffer, true) + serializeString(message, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities!.count)) + for item in entities! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 5) != 0 {media!.serialize(buffer, true)} + return (FunctionDescription(name: "messages.saveDraft", parameters: [("flags", String(describing: flags)), ("replyTo", String(describing: replyTo)), ("peer", String(describing: peer)), ("message", String(describing: message)), ("entities", String(describing: entities)), ("media", String(describing: media))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func saveGif(id: Api.InputDocument, unsave: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(846868683) + id.serialize(buffer, true) + unsave.serialize(buffer, true) + return (FunctionDescription(name: "messages.saveGif", parameters: [("id", String(describing: id)), ("unsave", String(describing: unsave))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func saveRecentSticker(flags: Int32, id: Api.InputDocument, unsave: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(958863608) + serializeInt32(flags, buffer: buffer, boxed: false) + id.serialize(buffer, true) + unsave.serialize(buffer, true) + return (FunctionDescription(name: "messages.saveRecentSticker", parameters: [("flags", String(describing: flags)), ("id", String(describing: id)), ("unsave", String(describing: unsave))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func search(flags: Int32, peer: Api.InputPeer, q: String, fromId: Api.InputPeer?, topMsgId: Int32?, filter: Api.MessagesFilter, minDate: Int32, maxDate: Int32, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1593989278) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeString(q, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {fromId!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} + filter.serialize(buffer, true) + serializeInt32(minDate, buffer: buffer, boxed: false) + serializeInt32(maxDate, buffer: buffer, boxed: false) + serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeInt32(addOffset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt32(maxId, buffer: buffer, boxed: false) + serializeInt32(minId, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.search", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("q", String(describing: q)), ("fromId", String(describing: fromId)), ("topMsgId", String(describing: topMsgId)), ("filter", String(describing: filter)), ("minDate", String(describing: minDate)), ("maxDate", String(describing: maxDate)), ("offsetId", String(describing: offsetId)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + let reader = BufferReader(buffer) + var result: Api.messages.Messages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Messages + } + return result + }) + } +} +public extension Api.functions.messages { + static func searchCustomEmoji(emoticon: String, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(739360983) + serializeString(emoticon, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.searchCustomEmoji", parameters: [("emoticon", String(describing: emoticon)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EmojiList? in + let reader = BufferReader(buffer) + var result: Api.EmojiList? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.EmojiList + } + return result + }) + } +} +public extension Api.functions.messages { + static func searchEmojiStickerSets(flags: Int32, q: String, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1833678516) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(q, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.searchEmojiStickerSets", parameters: [("flags", String(describing: flags)), ("q", String(describing: q)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.FoundStickerSets? in + let reader = BufferReader(buffer) + var result: Api.messages.FoundStickerSets? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.FoundStickerSets + } + return result + }) + } +} +public extension Api.functions.messages { + static func searchGlobal(flags: Int32, folderId: Int32?, q: String, filter: Api.MessagesFilter, minDate: Int32, maxDate: Int32, offsetRate: Int32, offsetPeer: Api.InputPeer, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1271290010) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} + serializeString(q, buffer: buffer, boxed: false) + filter.serialize(buffer, true) + serializeInt32(minDate, buffer: buffer, boxed: false) + serializeInt32(maxDate, buffer: buffer, boxed: false) + serializeInt32(offsetRate, buffer: buffer, boxed: false) + offsetPeer.serialize(buffer, true) + serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.searchGlobal", parameters: [("flags", String(describing: flags)), ("folderId", String(describing: folderId)), ("q", String(describing: q)), ("filter", String(describing: filter)), ("minDate", String(describing: minDate)), ("maxDate", String(describing: maxDate)), ("offsetRate", String(describing: offsetRate)), ("offsetPeer", String(describing: offsetPeer)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + let reader = BufferReader(buffer) + var result: Api.messages.Messages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Messages + } + return result + }) + } +} +public extension Api.functions.messages { + static func searchSentMedia(q: String, filter: Api.MessagesFilter, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(276705696) + serializeString(q, buffer: buffer, boxed: false) + filter.serialize(buffer, true) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.searchSentMedia", parameters: [("q", String(describing: q)), ("filter", String(describing: filter)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + let reader = BufferReader(buffer) + var result: Api.messages.Messages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Messages + } + return result + }) + } +} +public extension Api.functions.messages { + static func searchStickerSets(flags: Int32, q: String, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(896555914) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(q, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.searchStickerSets", parameters: [("flags", String(describing: flags)), ("q", String(describing: q)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.FoundStickerSets? in + let reader = BufferReader(buffer) + var result: Api.messages.FoundStickerSets? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.FoundStickerSets + } + return result + }) + } +} +public extension Api.functions.messages { + static func sendBotRequestedPeer(peer: Api.InputPeer, msgId: Int32, buttonId: Int32, requestedPeer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-29831141) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + serializeInt32(buttonId, buffer: buffer, boxed: false) + requestedPeer.serialize(buffer, true) + return (FunctionDescription(name: "messages.sendBotRequestedPeer", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("buttonId", String(describing: buttonId)), ("requestedPeer", String(describing: requestedPeer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func sendEncrypted(flags: Int32, peer: Api.InputEncryptedChat, randomId: Int64, data: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1157265941) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt64(randomId, buffer: buffer, boxed: false) + serializeBytes(data, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.sendEncrypted", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("randomId", String(describing: randomId)), ("data", String(describing: data))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SentEncryptedMessage? in + let reader = BufferReader(buffer) + var result: Api.messages.SentEncryptedMessage? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.SentEncryptedMessage + } + return result + }) + } +} +public extension Api.functions.messages { + static func sendEncryptedFile(flags: Int32, peer: Api.InputEncryptedChat, randomId: Int64, data: Buffer, file: Api.InputEncryptedFile) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1431914525) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt64(randomId, buffer: buffer, boxed: false) + serializeBytes(data, buffer: buffer, boxed: false) + file.serialize(buffer, true) + return (FunctionDescription(name: "messages.sendEncryptedFile", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("randomId", String(describing: randomId)), ("data", String(describing: data)), ("file", String(describing: file))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SentEncryptedMessage? in + let reader = BufferReader(buffer) + var result: Api.messages.SentEncryptedMessage? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.SentEncryptedMessage + } + return result + }) + } +} +public extension Api.functions.messages { + static func sendEncryptedService(peer: Api.InputEncryptedChat, randomId: Int64, data: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(852769188) + peer.serialize(buffer, true) + serializeInt64(randomId, buffer: buffer, boxed: false) + serializeBytes(data, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.sendEncryptedService", parameters: [("peer", String(describing: peer)), ("randomId", String(describing: randomId)), ("data", String(describing: data))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SentEncryptedMessage? in + let reader = BufferReader(buffer) + var result: Api.messages.SentEncryptedMessage? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.SentEncryptedMessage + } + return result + }) + } +} +public extension Api.functions.messages { + static func sendInlineBotResult(flags: Int32, peer: Api.InputPeer, replyTo: Api.InputReplyTo?, randomId: Int64, queryId: Int64, id: String, scheduleDate: Int32?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-138647366) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {replyTo!.serialize(buffer, true)} + serializeInt64(randomId, buffer: buffer, boxed: false) + serializeInt64(queryId, buffer: buffer, boxed: false) + serializeString(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)} + return (FunctionDescription(name: "messages.sendInlineBotResult", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("randomId", String(describing: randomId)), ("queryId", String(describing: queryId)), ("id", String(describing: id)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func sendMedia(flags: Int32, peer: Api.InputPeer, replyTo: Api.InputReplyTo?, media: Api.InputMedia, message: String, randomId: Int64, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1926021693) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {replyTo!.serialize(buffer, true)} + media.serialize(buffer, true) + serializeString(message, buffer: buffer, boxed: false) + serializeInt64(randomId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities!.count)) + for item in entities! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)} + return (FunctionDescription(name: "messages.sendMedia", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("media", String(describing: media)), ("message", String(describing: message)), ("randomId", String(describing: randomId)), ("replyMarkup", String(describing: replyMarkup)), ("entities", String(describing: entities)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func sendMessage(flags: Int32, peer: Api.InputPeer, replyTo: Api.InputReplyTo?, message: String, randomId: Int64, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(671943023) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {replyTo!.serialize(buffer, true)} + serializeString(message, buffer: buffer, boxed: false) + serializeInt64(randomId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities!.count)) + for item in entities! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)} + return (FunctionDescription(name: "messages.sendMessage", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("message", String(describing: message)), ("randomId", String(describing: randomId)), ("replyMarkup", String(describing: replyMarkup)), ("entities", String(describing: entities)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func sendMultiMedia(flags: Int32, peer: Api.InputPeer, replyTo: Api.InputReplyTo?, multiMedia: [Api.InputSingleMedia], scheduleDate: Int32?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1164872071) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {replyTo!.serialize(buffer, true)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(multiMedia.count)) + for item in multiMedia { + item.serialize(buffer, true) + } + if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)} + return (FunctionDescription(name: "messages.sendMultiMedia", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("multiMedia", String(describing: multiMedia)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func sendReaction(flags: Int32, peer: Api.InputPeer, msgId: Int32, reaction: [Api.Reaction]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-754091820) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(reaction!.count)) + for item in reaction! { + item.serialize(buffer, true) + }} + return (FunctionDescription(name: "messages.sendReaction", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("reaction", String(describing: reaction))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func sendScheduledMessages(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1120369398) + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "messages.sendScheduledMessages", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func sendScreenshotNotification(peer: Api.InputPeer, replyTo: Api.InputReplyTo, randomId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1589618665) + peer.serialize(buffer, true) + replyTo.serialize(buffer, true) + serializeInt64(randomId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.sendScreenshotNotification", parameters: [("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("randomId", String(describing: randomId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func sendVote(peer: Api.InputPeer, msgId: Int32, options: [Buffer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(283795844) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(options.count)) + for item in options { + serializeBytes(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "messages.sendVote", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("options", String(describing: options))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func sendWebViewData(bot: Api.InputUser, randomId: Int64, buttonText: String, data: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-603831608) + bot.serialize(buffer, true) + serializeInt64(randomId, buffer: buffer, boxed: false) + serializeString(buttonText, buffer: buffer, boxed: false) + serializeString(data, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.sendWebViewData", parameters: [("bot", String(describing: bot)), ("randomId", String(describing: randomId)), ("buttonText", String(describing: buttonText)), ("data", String(describing: data))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func sendWebViewResultMessage(botQueryId: String, result: Api.InputBotInlineResult) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(172168437) + serializeString(botQueryId, buffer: buffer, boxed: false) + result.serialize(buffer, true) + return (FunctionDescription(name: "messages.sendWebViewResultMessage", parameters: [("botQueryId", String(describing: botQueryId)), ("result", String(describing: result))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.WebViewMessageSent? in + let reader = BufferReader(buffer) + var result: Api.WebViewMessageSent? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.WebViewMessageSent + } + return result + }) + } +} +public extension Api.functions.messages { + static func setBotCallbackAnswer(flags: Int32, queryId: Int64, message: String?, url: String?, cacheTime: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-712043766) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(queryId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(message!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(url!, buffer: buffer, boxed: false)} + serializeInt32(cacheTime, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.setBotCallbackAnswer", parameters: [("flags", String(describing: flags)), ("queryId", String(describing: queryId)), ("message", String(describing: message)), ("url", String(describing: url)), ("cacheTime", String(describing: cacheTime))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func setBotPrecheckoutResults(flags: Int32, queryId: Int64, error: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(163765653) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(queryId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(error!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.setBotPrecheckoutResults", parameters: [("flags", String(describing: flags)), ("queryId", String(describing: queryId)), ("error", String(describing: error))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func setBotShippingResults(flags: Int32, queryId: Int64, error: String?, shippingOptions: [Api.ShippingOption]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-436833542) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(queryId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(error!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(shippingOptions!.count)) + for item in shippingOptions! { + item.serialize(buffer, true) + }} + return (FunctionDescription(name: "messages.setBotShippingResults", parameters: [("flags", String(describing: flags)), ("queryId", String(describing: queryId)), ("error", String(describing: error)), ("shippingOptions", String(describing: shippingOptions))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func setChatAvailableReactions(peer: Api.InputPeer, availableReactions: Api.ChatReactions) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-21928079) + peer.serialize(buffer, true) + availableReactions.serialize(buffer, true) + return (FunctionDescription(name: "messages.setChatAvailableReactions", parameters: [("peer", String(describing: peer)), ("availableReactions", String(describing: availableReactions))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func setChatTheme(peer: Api.InputPeer, emoticon: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-432283329) + 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 + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func setChatWallPaper(flags: Int32, peer: Api.InputPeer, wallpaper: Api.InputWallPaper?, settings: Api.WallPaperSettings?, id: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1879389471) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {wallpaper!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {settings!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(id!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.setChatWallPaper", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("wallpaper", String(describing: wallpaper)), ("settings", String(describing: settings)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func setDefaultHistoryTTL(period: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1632299963) + serializeInt32(period, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.setDefaultHistoryTTL", parameters: [("period", String(describing: period))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func setDefaultReaction(reaction: Api.Reaction) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1330094102) + reaction.serialize(buffer, true) + return (FunctionDescription(name: "messages.setDefaultReaction", parameters: [("reaction", String(describing: reaction))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func setEncryptedTyping(peer: Api.InputEncryptedChat, typing: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2031374829) + peer.serialize(buffer, true) + typing.serialize(buffer, true) + return (FunctionDescription(name: "messages.setEncryptedTyping", parameters: [("peer", String(describing: peer)), ("typing", String(describing: typing))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func setGameScore(flags: Int32, peer: Api.InputPeer, id: Int32, userId: Api.InputUser, score: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1896289088) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + userId.serialize(buffer, true) + serializeInt32(score, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.setGameScore", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id)), ("userId", String(describing: userId)), ("score", String(describing: score))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func setHistoryTTL(peer: Api.InputPeer, period: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1207017500) + peer.serialize(buffer, true) + serializeInt32(period, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.setHistoryTTL", parameters: [("peer", String(describing: peer)), ("period", String(describing: period))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func setInlineBotResults(flags: Int32, queryId: Int64, results: [Api.InputBotInlineResult], cacheTime: Int32, nextOffset: String?, switchPm: Api.InlineBotSwitchPM?, switchWebview: Api.InlineBotWebView?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1156406247) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(queryId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(results.count)) + for item in results { + item.serialize(buffer, true) + } + serializeInt32(cacheTime, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {switchPm!.serialize(buffer, true)} + if Int(flags) & Int(1 << 4) != 0 {switchWebview!.serialize(buffer, true)} + return (FunctionDescription(name: "messages.setInlineBotResults", parameters: [("flags", String(describing: flags)), ("queryId", String(describing: queryId)), ("results", String(describing: results)), ("cacheTime", String(describing: cacheTime)), ("nextOffset", String(describing: nextOffset)), ("switchPm", String(describing: switchPm)), ("switchWebview", String(describing: switchWebview))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func setInlineGameScore(flags: Int32, id: Api.InputBotInlineMessageID, userId: Api.InputUser, score: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(363700068) + serializeInt32(flags, buffer: buffer, boxed: false) + id.serialize(buffer, true) + userId.serialize(buffer, true) + serializeInt32(score, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.setInlineGameScore", parameters: [("flags", String(describing: flags)), ("id", String(describing: id)), ("userId", String(describing: userId)), ("score", String(describing: score))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func setTyping(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?, action: Api.SendMessageAction) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1486110434) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} + action.serialize(buffer, true) + return (FunctionDescription(name: "messages.setTyping", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId)), ("action", String(describing: action))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func startBot(bot: Api.InputUser, peer: Api.InputPeer, randomId: Int64, startParam: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-421563528) + bot.serialize(buffer, true) + peer.serialize(buffer, true) + serializeInt64(randomId, buffer: buffer, boxed: false) + serializeString(startParam, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.startBot", parameters: [("bot", String(describing: bot)), ("peer", String(describing: peer)), ("randomId", String(describing: randomId)), ("startParam", String(describing: startParam))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func startHistoryImport(peer: Api.InputPeer, importId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1271008444) + peer.serialize(buffer, true) + serializeInt64(importId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.startHistoryImport", parameters: [("peer", String(describing: peer)), ("importId", String(describing: importId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func toggleBotInAttachMenu(flags: Int32, bot: Api.InputUser, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1777704297) + serializeInt32(flags, buffer: buffer, boxed: false) + bot.serialize(buffer, true) + enabled.serialize(buffer, true) + return (FunctionDescription(name: "messages.toggleBotInAttachMenu", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func toggleDialogPin(flags: Int32, peer: Api.InputDialogPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1489903017) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + return (FunctionDescription(name: "messages.toggleDialogPin", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func toggleNoForwards(peer: Api.InputPeer, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1323389022) + peer.serialize(buffer, true) + enabled.serialize(buffer, true) + return (FunctionDescription(name: "messages.toggleNoForwards", parameters: [("peer", String(describing: peer)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func togglePeerTranslations(flags: Int32, peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-461589127) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + return (FunctionDescription(name: "messages.togglePeerTranslations", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func toggleStickerSets(flags: Int32, stickersets: [Api.InputStickerSet]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1257951254) + serializeInt32(flags, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(stickersets.count)) + for item in stickersets { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "messages.toggleStickerSets", parameters: [("flags", String(describing: flags)), ("stickersets", String(describing: stickersets))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func transcribeAudio(peer: Api.InputPeer, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(647928393) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.transcribeAudio", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.TranscribedAudio? in + let reader = BufferReader(buffer) + var result: Api.messages.TranscribedAudio? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.TranscribedAudio + } + return result + }) + } +} +public extension Api.functions.messages { + static func translateText(flags: Int32, peer: Api.InputPeer?, id: [Int32]?, text: [Api.TextWithEntities]?, toLang: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1662529584) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {peer!.serialize(buffer, true)} + if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id!.count)) + for item in id! { + serializeInt32(item, buffer: buffer, boxed: false) + }} + if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(text!.count)) + for item in text! { + item.serialize(buffer, true) + }} + serializeString(toLang, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.translateText", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id)), ("text", String(describing: text)), ("toLang", String(describing: toLang))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.TranslatedText? in + let reader = BufferReader(buffer) + var result: Api.messages.TranslatedText? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.TranslatedText + } + return result + }) + } +} +public extension Api.functions.messages { + static func uninstallStickerSet(stickerset: Api.InputStickerSet) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-110209570) + stickerset.serialize(buffer, true) + return (FunctionDescription(name: "messages.uninstallStickerSet", parameters: [("stickerset", String(describing: stickerset))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func unpinAllMessages(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-299714136) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.unpinAllMessages", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedHistory? in + let reader = BufferReader(buffer) + var result: Api.messages.AffectedHistory? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.AffectedHistory + } + return result + }) + } +} +public extension Api.functions.messages { + static func updateDialogFilter(flags: Int32, id: Int32, filter: Api.DialogFilter?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(450142282) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {filter!.serialize(buffer, true)} + return (FunctionDescription(name: "messages.updateDialogFilter", parameters: [("flags", String(describing: flags)), ("id", String(describing: id)), ("filter", String(describing: filter))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func updateDialogFiltersOrder(order: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-983318044) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order.count)) + for item in order { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "messages.updateDialogFiltersOrder", parameters: [("order", String(describing: order))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.messages { + static func updatePinnedMessage(flags: Int32, peer: Api.InputPeer, id: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-760547348) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.updatePinnedMessage", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.messages { + static func uploadEncryptedFile(peer: Api.InputEncryptedChat, file: Api.InputEncryptedFile) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1347929239) + peer.serialize(buffer, true) + file.serialize(buffer, true) + return (FunctionDescription(name: "messages.uploadEncryptedFile", parameters: [("peer", String(describing: peer)), ("file", String(describing: file))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EncryptedFile? in + let reader = BufferReader(buffer) + var result: Api.EncryptedFile? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.EncryptedFile + } + return result + }) + } +} +public extension Api.functions.messages { + static func uploadImportedMedia(peer: Api.InputPeer, importId: Int64, fileName: String, media: Api.InputMedia) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(713433234) + peer.serialize(buffer, true) + serializeInt64(importId, buffer: buffer, boxed: false) + serializeString(fileName, buffer: buffer, boxed: false) + media.serialize(buffer, true) + return (FunctionDescription(name: "messages.uploadImportedMedia", parameters: [("peer", String(describing: peer)), ("importId", String(describing: importId)), ("fileName", String(describing: fileName)), ("media", String(describing: media))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.MessageMedia? in + let reader = BufferReader(buffer) + var result: Api.MessageMedia? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.MessageMedia + } + return result + }) + } +} +public extension Api.functions.messages { + static func uploadMedia(peer: Api.InputPeer, media: Api.InputMedia) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1369162417) + peer.serialize(buffer, true) + media.serialize(buffer, true) + return (FunctionDescription(name: "messages.uploadMedia", parameters: [("peer", String(describing: peer)), ("media", String(describing: media))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.MessageMedia? in + let reader = BufferReader(buffer) + var result: Api.MessageMedia? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.MessageMedia + } + return result + }) + } +} +public extension Api.functions.payments { + static func applyGiftCode(slug: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-152934316) + serializeString(slug, buffer: buffer, boxed: false) + return (FunctionDescription(name: "payments.applyGiftCode", parameters: [("slug", String(describing: slug))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.payments { + static func assignAppStoreTransaction(receipt: Buffer, purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-2131921795) + serializeBytes(receipt, buffer: buffer, boxed: false) + purpose.serialize(buffer, true) + return (FunctionDescription(name: "payments.assignAppStoreTransaction", parameters: [("receipt", String(describing: receipt)), ("purpose", String(describing: purpose))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.payments { + static func assignPlayMarketTransaction(receipt: Api.DataJSON, purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-537046829) + receipt.serialize(buffer, true) + purpose.serialize(buffer, true) + return (FunctionDescription(name: "payments.assignPlayMarketTransaction", parameters: [("receipt", String(describing: receipt)), ("purpose", String(describing: purpose))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.payments { + static func canPurchasePremium(purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1614700874) + purpose.serialize(buffer, true) + return (FunctionDescription(name: "payments.canPurchasePremium", parameters: [("purpose", String(describing: purpose))]), 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.payments { + static func checkGiftCode(slug: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1907247935) + serializeString(slug, buffer: buffer, boxed: false) + return (FunctionDescription(name: "payments.checkGiftCode", parameters: [("slug", String(describing: slug))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.CheckedGiftCode? in + let reader = BufferReader(buffer) + var result: Api.payments.CheckedGiftCode? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.payments.CheckedGiftCode + } + return result + }) + } +} +public extension Api.functions.payments { + static func clearSavedInfo(flags: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-667062079) + serializeInt32(flags, buffer: buffer, boxed: false) + return (FunctionDescription(name: "payments.clearSavedInfo", parameters: [("flags", String(describing: flags))]), 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.payments { + static func exportInvoice(invoiceMedia: Api.InputMedia) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(261206117) + invoiceMedia.serialize(buffer, true) + return (FunctionDescription(name: "payments.exportInvoice", parameters: [("invoiceMedia", String(describing: invoiceMedia))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.ExportedInvoice? in + let reader = BufferReader(buffer) + var result: Api.payments.ExportedInvoice? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.payments.ExportedInvoice + } + return result + }) + } +} +public extension Api.functions.payments { + static func getBankCardData(number: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(779736953) + serializeString(number, buffer: buffer, boxed: false) + return (FunctionDescription(name: "payments.getBankCardData", parameters: [("number", String(describing: number))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.BankCardData? in + let reader = BufferReader(buffer) + var result: Api.payments.BankCardData? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.payments.BankCardData + } + return result + }) + } +} +public extension Api.functions.payments { + static func getGiveawayInfo(peer: Api.InputPeer, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-198994907) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "payments.getGiveawayInfo", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.GiveawayInfo? in + let reader = BufferReader(buffer) + var result: Api.payments.GiveawayInfo? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.payments.GiveawayInfo + } + return result + }) + } +} +public extension Api.functions.payments { + static func getPaymentForm(flags: Int32, invoice: Api.InputInvoice, themeParams: Api.DataJSON?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(924093883) + serializeInt32(flags, buffer: buffer, boxed: false) + invoice.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {themeParams!.serialize(buffer, true)} + return (FunctionDescription(name: "payments.getPaymentForm", parameters: [("flags", String(describing: flags)), ("invoice", String(describing: invoice)), ("themeParams", String(describing: themeParams))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.PaymentForm? in + let reader = BufferReader(buffer) + var result: Api.payments.PaymentForm? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.payments.PaymentForm + } + return result + }) + } +} +public extension Api.functions.payments { + static func getPaymentReceipt(peer: Api.InputPeer, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(611897804) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "payments.getPaymentReceipt", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.PaymentReceipt? in + let reader = BufferReader(buffer) + var result: Api.payments.PaymentReceipt? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.payments.PaymentReceipt + } + return result + }) + } +} +public extension Api.functions.payments { + static func getPremiumGiftCodeOptions(flags: Int32, boostPeer: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.PremiumGiftCodeOption]>) { + let buffer = Buffer() + buffer.appendInt32(660060756) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {boostPeer!.serialize(buffer, true)} + return (FunctionDescription(name: "payments.getPremiumGiftCodeOptions", parameters: [("flags", String(describing: flags)), ("boostPeer", String(describing: boostPeer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.PremiumGiftCodeOption]? in + let reader = BufferReader(buffer) + var result: [Api.PremiumGiftCodeOption]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.PremiumGiftCodeOption.self) + } + return result + }) + } +} +public extension Api.functions.payments { + static func getSavedInfo() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(578650699) + + return (FunctionDescription(name: "payments.getSavedInfo", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.SavedInfo? in + let reader = BufferReader(buffer) + var result: Api.payments.SavedInfo? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.payments.SavedInfo + } + return result + }) + } +} +public extension Api.functions.payments { + static func launchPrepaidGiveaway(peer: Api.InputPeer, giveawayId: Int64, purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1609928480) + peer.serialize(buffer, true) + serializeInt64(giveawayId, buffer: buffer, boxed: false) + purpose.serialize(buffer, true) + return (FunctionDescription(name: "payments.launchPrepaidGiveaway", parameters: [("peer", String(describing: peer)), ("giveawayId", String(describing: giveawayId)), ("purpose", String(describing: purpose))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.payments { + static func sendPaymentForm(flags: Int32, formId: Int64, invoice: Api.InputInvoice, requestedInfoId: String?, shippingOptionId: String?, credentials: Api.InputPaymentCredentials, tipAmount: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(755192367) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(formId, buffer: buffer, boxed: false) + invoice.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeString(requestedInfoId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(shippingOptionId!, buffer: buffer, boxed: false)} + credentials.serialize(buffer, true) + if Int(flags) & Int(1 << 2) != 0 {serializeInt64(tipAmount!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "payments.sendPaymentForm", parameters: [("flags", String(describing: flags)), ("formId", String(describing: formId)), ("invoice", String(describing: invoice)), ("requestedInfoId", String(describing: requestedInfoId)), ("shippingOptionId", String(describing: shippingOptionId)), ("credentials", String(describing: credentials)), ("tipAmount", String(describing: tipAmount))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.PaymentResult? in + let reader = BufferReader(buffer) + var result: Api.payments.PaymentResult? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.payments.PaymentResult + } + return result + }) + } +} +public extension Api.functions.payments { + static func validateRequestedInfo(flags: Int32, invoice: Api.InputInvoice, info: Api.PaymentRequestedInfo) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1228345045) + serializeInt32(flags, buffer: buffer, boxed: false) + invoice.serialize(buffer, true) + info.serialize(buffer, true) + return (FunctionDescription(name: "payments.validateRequestedInfo", parameters: [("flags", String(describing: flags)), ("invoice", String(describing: invoice)), ("info", String(describing: info))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.ValidatedRequestedInfo? in + let reader = BufferReader(buffer) + var result: Api.payments.ValidatedRequestedInfo? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.payments.ValidatedRequestedInfo + } + return result + }) + } +} +public extension Api.functions.phone { + static func acceptCall(peer: Api.InputPhoneCall, gB: Buffer, `protocol`: Api.PhoneCallProtocol) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1003664544) + peer.serialize(buffer, true) + serializeBytes(gB, buffer: buffer, boxed: false) + `protocol`.serialize(buffer, true) + return (FunctionDescription(name: "phone.acceptCall", parameters: [("peer", String(describing: peer)), ("gB", String(describing: gB)), ("`protocol`", String(describing: `protocol`))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.PhoneCall? in + let reader = BufferReader(buffer) + var result: Api.phone.PhoneCall? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.phone.PhoneCall + } + return result + }) + } +} +public extension Api.functions.phone { + static func checkGroupCall(call: Api.InputGroupCall, sources: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int32]>) { + let buffer = Buffer() + buffer.appendInt32(-1248003721) + call.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(sources.count)) + for item in sources { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "phone.checkGroupCall", parameters: [("call", String(describing: call)), ("sources", String(describing: sources))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int32]? in + let reader = BufferReader(buffer) + var result: [Int32]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + return result + }) + } +} +public extension Api.functions.phone { + static func confirmCall(peer: Api.InputPhoneCall, gA: Buffer, keyFingerprint: Int64, `protocol`: Api.PhoneCallProtocol) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(788404002) + peer.serialize(buffer, true) + serializeBytes(gA, buffer: buffer, boxed: false) + serializeInt64(keyFingerprint, buffer: buffer, boxed: false) + `protocol`.serialize(buffer, true) + return (FunctionDescription(name: "phone.confirmCall", parameters: [("peer", String(describing: peer)), ("gA", String(describing: gA)), ("keyFingerprint", String(describing: keyFingerprint)), ("`protocol`", String(describing: `protocol`))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.PhoneCall? in + let reader = BufferReader(buffer) + var result: Api.phone.PhoneCall? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.phone.PhoneCall + } + return result + }) + } +} +public extension Api.functions.phone { + static func createGroupCall(flags: Int32, peer: Api.InputPeer, randomId: Int32, title: String?, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1221445336) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(randomId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "phone.createGroupCall", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("randomId", String(describing: randomId)), ("title", String(describing: title)), ("scheduleDate", String(describing: scheduleDate))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.phone { + static func discardCall(flags: Int32, peer: Api.InputPhoneCall, duration: Int32, reason: Api.PhoneCallDiscardReason, connectionId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1295269440) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(duration, buffer: buffer, boxed: false) + reason.serialize(buffer, true) + serializeInt64(connectionId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "phone.discardCall", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("duration", String(describing: duration)), ("reason", String(describing: reason)), ("connectionId", String(describing: connectionId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.phone { + static func discardGroupCall(call: Api.InputGroupCall) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2054648117) + call.serialize(buffer, true) + return (FunctionDescription(name: "phone.discardGroupCall", parameters: [("call", String(describing: call))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.phone { + static func editGroupCallParticipant(flags: Int32, call: Api.InputGroupCall, participant: Api.InputPeer, muted: Api.Bool?, volume: Int32?, raiseHand: Api.Bool?, videoStopped: Api.Bool?, videoPaused: Api.Bool?, presentationPaused: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1524155713) + serializeInt32(flags, buffer: buffer, boxed: false) + call.serialize(buffer, true) + participant.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {muted!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(volume!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {raiseHand!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {videoStopped!.serialize(buffer, true)} + if Int(flags) & Int(1 << 4) != 0 {videoPaused!.serialize(buffer, true)} + if Int(flags) & Int(1 << 5) != 0 {presentationPaused!.serialize(buffer, true)} + return (FunctionDescription(name: "phone.editGroupCallParticipant", parameters: [("flags", String(describing: flags)), ("call", String(describing: call)), ("participant", String(describing: participant)), ("muted", String(describing: muted)), ("volume", String(describing: volume)), ("raiseHand", String(describing: raiseHand)), ("videoStopped", String(describing: videoStopped)), ("videoPaused", String(describing: videoPaused)), ("presentationPaused", String(describing: presentationPaused))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.phone { + static func editGroupCallTitle(call: Api.InputGroupCall, title: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(480685066) + call.serialize(buffer, true) + serializeString(title, buffer: buffer, boxed: false) + return (FunctionDescription(name: "phone.editGroupCallTitle", parameters: [("call", String(describing: call)), ("title", String(describing: title))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.phone { + static func exportGroupCallInvite(flags: Int32, call: Api.InputGroupCall) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-425040769) + serializeInt32(flags, buffer: buffer, boxed: false) + call.serialize(buffer, true) + return (FunctionDescription(name: "phone.exportGroupCallInvite", parameters: [("flags", String(describing: flags)), ("call", String(describing: call))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.ExportedGroupCallInvite? in + let reader = BufferReader(buffer) + var result: Api.phone.ExportedGroupCallInvite? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.phone.ExportedGroupCallInvite + } + return result + }) + } +} +public extension Api.functions.phone { + static func getCallConfig() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1430593449) + + return (FunctionDescription(name: "phone.getCallConfig", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.DataJSON? in + let reader = BufferReader(buffer) + var result: Api.DataJSON? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.DataJSON + } + return result + }) + } +} +public extension Api.functions.phone { + static func getGroupCall(call: Api.InputGroupCall, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(68699611) + call.serialize(buffer, true) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "phone.getGroupCall", parameters: [("call", String(describing: call)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.GroupCall? in + let reader = BufferReader(buffer) + var result: Api.phone.GroupCall? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.phone.GroupCall + } + return result + }) + } +} +public extension Api.functions.phone { + static func getGroupCallJoinAs(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-277077702) + peer.serialize(buffer, true) + return (FunctionDescription(name: "phone.getGroupCallJoinAs", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.JoinAsPeers? in + let reader = BufferReader(buffer) + var result: Api.phone.JoinAsPeers? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.phone.JoinAsPeers + } + return result + }) + } +} +public extension Api.functions.phone { + static func getGroupCallStreamChannels(call: Api.InputGroupCall) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(447879488) + call.serialize(buffer, true) + return (FunctionDescription(name: "phone.getGroupCallStreamChannels", parameters: [("call", String(describing: call))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.GroupCallStreamChannels? in + let reader = BufferReader(buffer) + var result: Api.phone.GroupCallStreamChannels? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.phone.GroupCallStreamChannels + } + return result + }) + } +} +public extension Api.functions.phone { + static func getGroupCallStreamRtmpUrl(peer: Api.InputPeer, revoke: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-558650433) + peer.serialize(buffer, true) + revoke.serialize(buffer, true) + return (FunctionDescription(name: "phone.getGroupCallStreamRtmpUrl", parameters: [("peer", String(describing: peer)), ("revoke", String(describing: revoke))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.GroupCallStreamRtmpUrl? in + let reader = BufferReader(buffer) + var result: Api.phone.GroupCallStreamRtmpUrl? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.phone.GroupCallStreamRtmpUrl + } + return result + }) + } +} +public extension Api.functions.phone { + static func getGroupParticipants(call: Api.InputGroupCall, ids: [Api.InputPeer], sources: [Int32], offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-984033109) + call.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(ids.count)) + for item in ids { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(sources.count)) + for item in sources { + serializeInt32(item, buffer: buffer, boxed: false) + } + serializeString(offset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "phone.getGroupParticipants", parameters: [("call", String(describing: call)), ("ids", String(describing: ids)), ("sources", String(describing: sources)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.GroupParticipants? in + let reader = BufferReader(buffer) + var result: Api.phone.GroupParticipants? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.phone.GroupParticipants + } + return result + }) + } +} +public extension Api.functions.phone { + static func inviteToGroupCall(call: Api.InputGroupCall, users: [Api.InputUser]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2067345760) + call.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "phone.inviteToGroupCall", parameters: [("call", String(describing: call)), ("users", String(describing: users))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.phone { + static func joinGroupCall(flags: Int32, call: Api.InputGroupCall, joinAs: Api.InputPeer, inviteHash: String?, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1322057861) + serializeInt32(flags, buffer: buffer, boxed: false) + call.serialize(buffer, true) + joinAs.serialize(buffer, true) + if Int(flags) & Int(1 << 1) != 0 {serializeString(inviteHash!, buffer: buffer, boxed: false)} + params.serialize(buffer, true) + return (FunctionDescription(name: "phone.joinGroupCall", parameters: [("flags", String(describing: flags)), ("call", String(describing: call)), ("joinAs", String(describing: joinAs)), ("inviteHash", String(describing: inviteHash)), ("params", String(describing: params))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.phone { + static func joinGroupCallPresentation(call: Api.InputGroupCall, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-873829436) + call.serialize(buffer, true) + params.serialize(buffer, true) + return (FunctionDescription(name: "phone.joinGroupCallPresentation", parameters: [("call", String(describing: call)), ("params", String(describing: params))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.phone { + static func leaveGroupCall(call: Api.InputGroupCall, source: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1342404601) + call.serialize(buffer, true) + serializeInt32(source, buffer: buffer, boxed: false) + return (FunctionDescription(name: "phone.leaveGroupCall", parameters: [("call", String(describing: call)), ("source", String(describing: source))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.phone { + static func leaveGroupCallPresentation(call: Api.InputGroupCall) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(475058500) + call.serialize(buffer, true) + return (FunctionDescription(name: "phone.leaveGroupCallPresentation", parameters: [("call", String(describing: call))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.phone { + static func receivedCall(peer: Api.InputPhoneCall) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(399855457) + peer.serialize(buffer, true) + return (FunctionDescription(name: "phone.receivedCall", parameters: [("peer", String(describing: peer))]), 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.phone { + static func requestCall(flags: Int32, userId: Api.InputUser, randomId: Int32, gAHash: Buffer, `protocol`: Api.PhoneCallProtocol) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1124046573) + serializeInt32(flags, buffer: buffer, boxed: false) + userId.serialize(buffer, true) + serializeInt32(randomId, buffer: buffer, boxed: false) + serializeBytes(gAHash, buffer: buffer, boxed: false) + `protocol`.serialize(buffer, true) + return (FunctionDescription(name: "phone.requestCall", parameters: [("flags", String(describing: flags)), ("userId", String(describing: userId)), ("randomId", String(describing: randomId)), ("gAHash", String(describing: gAHash)), ("`protocol`", String(describing: `protocol`))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.PhoneCall? in + let reader = BufferReader(buffer) + var result: Api.phone.PhoneCall? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.phone.PhoneCall + } + return result + }) + } +} +public extension Api.functions.phone { + static func saveCallDebug(peer: Api.InputPhoneCall, debug: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(662363518) + peer.serialize(buffer, true) + debug.serialize(buffer, true) + return (FunctionDescription(name: "phone.saveCallDebug", parameters: [("peer", String(describing: peer)), ("debug", String(describing: debug))]), 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.phone { + static func saveCallLog(peer: Api.InputPhoneCall, file: Api.InputFile) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1092913030) + peer.serialize(buffer, true) + file.serialize(buffer, true) + return (FunctionDescription(name: "phone.saveCallLog", parameters: [("peer", String(describing: peer)), ("file", String(describing: file))]), 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.phone { + static func saveDefaultGroupCallJoinAs(peer: Api.InputPeer, joinAs: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1465786252) + peer.serialize(buffer, true) + joinAs.serialize(buffer, true) + return (FunctionDescription(name: "phone.saveDefaultGroupCallJoinAs", parameters: [("peer", String(describing: peer)), ("joinAs", String(describing: joinAs))]), 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.phone { + static func sendSignalingData(peer: Api.InputPhoneCall, data: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-8744061) + peer.serialize(buffer, true) + serializeBytes(data, buffer: buffer, boxed: false) + return (FunctionDescription(name: "phone.sendSignalingData", parameters: [("peer", String(describing: peer)), ("data", String(describing: data))]), 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.phone { + static func setCallRating(flags: Int32, peer: Api.InputPhoneCall, rating: Int32, comment: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1508562471) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(rating, buffer: buffer, boxed: false) + serializeString(comment, buffer: buffer, boxed: false) + return (FunctionDescription(name: "phone.setCallRating", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("rating", String(describing: rating)), ("comment", String(describing: comment))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.phone { + static func startScheduledGroupCall(call: Api.InputGroupCall) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1451287362) + call.serialize(buffer, true) + return (FunctionDescription(name: "phone.startScheduledGroupCall", parameters: [("call", String(describing: call))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.phone { + static func toggleGroupCallRecord(flags: Int32, call: Api.InputGroupCall, title: String?, videoPortrait: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-248985848) + serializeInt32(flags, buffer: buffer, boxed: false) + call.serialize(buffer, true) + if Int(flags) & Int(1 << 1) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {videoPortrait!.serialize(buffer, true)} + return (FunctionDescription(name: "phone.toggleGroupCallRecord", parameters: [("flags", String(describing: flags)), ("call", String(describing: call)), ("title", String(describing: title)), ("videoPortrait", String(describing: videoPortrait))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.phone { + static func toggleGroupCallSettings(flags: Int32, call: Api.InputGroupCall, joinMuted: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1958458429) + serializeInt32(flags, buffer: buffer, boxed: false) + call.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {joinMuted!.serialize(buffer, true)} + return (FunctionDescription(name: "phone.toggleGroupCallSettings", parameters: [("flags", String(describing: flags)), ("call", String(describing: call)), ("joinMuted", String(describing: joinMuted))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.phone { + static func toggleGroupCallStartSubscription(call: Api.InputGroupCall, subscribed: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(563885286) + call.serialize(buffer, true) + subscribed.serialize(buffer, true) + return (FunctionDescription(name: "phone.toggleGroupCallStartSubscription", parameters: [("call", String(describing: call)), ("subscribed", String(describing: subscribed))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.photos { + static func deletePhotos(id: [Api.InputPhoto]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int64]>) { + let buffer = Buffer() + buffer.appendInt32(-2016444625) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "photos.deletePhotos", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int64]? in + let reader = BufferReader(buffer) + var result: [Int64]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } + return result + }) + } +} +public extension Api.functions.photos { + static func getUserPhotos(userId: Api.InputUser, offset: Int32, maxId: Int64, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1848823128) + userId.serialize(buffer, true) + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt64(maxId, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "photos.getUserPhotos", parameters: [("userId", String(describing: userId)), ("offset", String(describing: offset)), ("maxId", String(describing: maxId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photos? in + let reader = BufferReader(buffer) + var result: Api.photos.Photos? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.photos.Photos + } + return result + }) + } +} +public extension Api.functions.photos { + static func updateProfilePhoto(flags: Int32, bot: Api.InputUser?, id: Api.InputPhoto) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(166207545) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {bot!.serialize(buffer, true)} + id.serialize(buffer, true) + return (FunctionDescription(name: "photos.updateProfilePhoto", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in + let reader = BufferReader(buffer) + var result: Api.photos.Photo? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.photos.Photo + } + return result + }) + } +} +public extension Api.functions.photos { + static func uploadContactProfilePhoto(flags: Int32, userId: Api.InputUser, file: Api.InputFile?, video: Api.InputFile?, videoStartTs: Double?, videoEmojiMarkup: Api.VideoSize?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-515093903) + serializeInt32(flags, buffer: buffer, boxed: false) + userId.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {file!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {video!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {serializeDouble(videoStartTs!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 5) != 0 {videoEmojiMarkup!.serialize(buffer, true)} + return (FunctionDescription(name: "photos.uploadContactProfilePhoto", parameters: [("flags", String(describing: flags)), ("userId", String(describing: userId)), ("file", String(describing: file)), ("video", String(describing: video)), ("videoStartTs", String(describing: videoStartTs)), ("videoEmojiMarkup", String(describing: videoEmojiMarkup))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in + let reader = BufferReader(buffer) + var result: Api.photos.Photo? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.photos.Photo + } + return result + }) + } +} +public extension Api.functions.photos { + static func uploadProfilePhoto(flags: Int32, bot: Api.InputUser?, file: Api.InputFile?, video: Api.InputFile?, videoStartTs: Double?, videoEmojiMarkup: Api.VideoSize?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(59286453) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 5) != 0 {bot!.serialize(buffer, true)} + if Int(flags) & Int(1 << 0) != 0 {file!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {video!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {serializeDouble(videoStartTs!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {videoEmojiMarkup!.serialize(buffer, true)} + return (FunctionDescription(name: "photos.uploadProfilePhoto", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("file", String(describing: file)), ("video", String(describing: video)), ("videoStartTs", String(describing: videoStartTs)), ("videoEmojiMarkup", String(describing: videoEmojiMarkup))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in + let reader = BufferReader(buffer) + var result: Api.photos.Photo? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.photos.Photo + } + return result + }) + } +} +public extension Api.functions.premium { + static func applyBoost(flags: Int32, slots: [Int32]?, peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1803396934) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(slots!.count)) + for item in slots! { + serializeInt32(item, buffer: buffer, boxed: false) + }} + peer.serialize(buffer, true) + return (FunctionDescription(name: "premium.applyBoost", parameters: [("flags", String(describing: flags)), ("slots", String(describing: slots)), ("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.premium.MyBoosts? in + let reader = BufferReader(buffer) + var result: Api.premium.MyBoosts? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.premium.MyBoosts + } + return result + }) + } +} +public extension Api.functions.premium { + static func getBoostsList(flags: Int32, peer: Api.InputPeer, offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1626764896) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeString(offset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "premium.getBoostsList", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.premium.BoostsList? in + let reader = BufferReader(buffer) + var result: Api.premium.BoostsList? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.premium.BoostsList + } + return result + }) + } +} +public extension Api.functions.premium { + static func getBoostsStatus(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(70197089) + peer.serialize(buffer, true) + return (FunctionDescription(name: "premium.getBoostsStatus", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.premium.BoostsStatus? in + let reader = BufferReader(buffer) + var result: Api.premium.BoostsStatus? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.premium.BoostsStatus + } + return result + }) + } +} +public extension Api.functions.premium { + static func getMyBoosts() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(199719754) + + return (FunctionDescription(name: "premium.getMyBoosts", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.premium.MyBoosts? in + let reader = BufferReader(buffer) + var result: Api.premium.MyBoosts? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.premium.MyBoosts + } + return result + }) + } +} +public extension Api.functions.premium { + static func getUserBoosts(peer: Api.InputPeer, userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(965037343) + peer.serialize(buffer, true) + userId.serialize(buffer, true) + return (FunctionDescription(name: "premium.getUserBoosts", parameters: [("peer", String(describing: peer)), ("userId", String(describing: userId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.premium.BoostsList? in + let reader = BufferReader(buffer) + var result: Api.premium.BoostsList? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.premium.BoostsList + } + return result + }) + } +} +public extension Api.functions.stats { + static func getBroadcastStats(flags: Int32, channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1421720550) + serializeInt32(flags, buffer: buffer, boxed: false) + channel.serialize(buffer, true) + return (FunctionDescription(name: "stats.getBroadcastStats", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stats.BroadcastStats? in + let reader = BufferReader(buffer) + var result: Api.stats.BroadcastStats? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.stats.BroadcastStats + } + return result + }) + } +} +public extension Api.functions.stats { + static func getMegagroupStats(flags: Int32, channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-589330937) + serializeInt32(flags, buffer: buffer, boxed: false) + channel.serialize(buffer, true) + return (FunctionDescription(name: "stats.getMegagroupStats", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stats.MegagroupStats? in + let reader = BufferReader(buffer) + var result: Api.stats.MegagroupStats? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.stats.MegagroupStats + } + return result + }) + } +} +public extension Api.functions.stats { + static func getMessagePublicForwards(channel: Api.InputChannel, msgId: Int32, offsetRate: Int32, offsetPeer: Api.InputPeer, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1445996571) + channel.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + serializeInt32(offsetRate, buffer: buffer, boxed: false) + offsetPeer.serialize(buffer, true) + serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stats.getMessagePublicForwards", parameters: [("channel", String(describing: channel)), ("msgId", String(describing: msgId)), ("offsetRate", String(describing: offsetRate)), ("offsetPeer", String(describing: offsetPeer)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + let reader = BufferReader(buffer) + var result: Api.messages.Messages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Messages + } + return result + }) + } +} +public extension Api.functions.stats { + static func getMessageStats(flags: Int32, channel: Api.InputChannel, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1226791947) + serializeInt32(flags, buffer: buffer, boxed: false) + channel.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stats.getMessageStats", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stats.MessageStats? in + let reader = BufferReader(buffer) + var result: Api.stats.MessageStats? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.stats.MessageStats + } + return result + }) + } +} +public extension Api.functions.stats { + static func getStoryPublicForwards(peer: Api.InputPeer, id: Int32, offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1505526026) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + serializeString(offset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stats.getStoryPublicForwards", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stats.PublicForwards? in + let reader = BufferReader(buffer) + var result: Api.stats.PublicForwards? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.stats.PublicForwards + } + return result + }) + } +} +public extension Api.functions.stats { + static func getStoryStats(flags: Int32, peer: Api.InputPeer, id: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(927985472) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stats.getStoryStats", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stats.StoryStats? in + let reader = BufferReader(buffer) + var result: Api.stats.StoryStats? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.stats.StoryStats + } + return result + }) + } +} +public extension Api.functions.stats { + static func loadAsyncGraph(flags: Int32, token: String, x: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1646092192) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(token, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt64(x!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "stats.loadAsyncGraph", parameters: [("flags", String(describing: flags)), ("token", String(describing: token)), ("x", String(describing: x))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.StatsGraph? in + let reader = BufferReader(buffer) + var result: Api.StatsGraph? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + return result + }) + } +} +public extension Api.functions.stickers { + static func addStickerToSet(stickerset: Api.InputStickerSet, sticker: Api.InputStickerSetItem) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-2041315650) + stickerset.serialize(buffer, true) + sticker.serialize(buffer, true) + return (FunctionDescription(name: "stickers.addStickerToSet", parameters: [("stickerset", String(describing: stickerset)), ("sticker", String(describing: sticker))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in + let reader = BufferReader(buffer) + var result: Api.messages.StickerSet? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.StickerSet + } + return result + }) + } +} +public extension Api.functions.stickers { + static func changeSticker(flags: Int32, sticker: Api.InputDocument, emoji: String?, maskCoords: Api.MaskCoords?, keywords: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-179077444) + serializeInt32(flags, buffer: buffer, boxed: false) + sticker.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeString(emoji!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {maskCoords!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(keywords!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "stickers.changeSticker", parameters: [("flags", String(describing: flags)), ("sticker", String(describing: sticker)), ("emoji", String(describing: emoji)), ("maskCoords", String(describing: maskCoords)), ("keywords", String(describing: keywords))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in + let reader = BufferReader(buffer) + var result: Api.messages.StickerSet? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.StickerSet + } + return result + }) + } +} +public extension Api.functions.stickers { + static func changeStickerPosition(sticker: Api.InputDocument, position: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-4795190) + sticker.serialize(buffer, true) + serializeInt32(position, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stickers.changeStickerPosition", parameters: [("sticker", String(describing: sticker)), ("position", String(describing: position))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in + let reader = BufferReader(buffer) + var result: Api.messages.StickerSet? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.StickerSet + } + return result + }) + } +} +public extension Api.functions.stickers { + static func checkShortName(shortName: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(676017721) + serializeString(shortName, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stickers.checkShortName", parameters: [("shortName", String(describing: shortName))]), 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.stickers { + static func createStickerSet(flags: Int32, userId: Api.InputUser, title: String, shortName: String, thumb: Api.InputDocument?, stickers: [Api.InputStickerSetItem], software: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1876841625) + serializeInt32(flags, buffer: buffer, boxed: false) + userId.serialize(buffer, true) + serializeString(title, buffer: buffer, boxed: false) + serializeString(shortName, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {thumb!.serialize(buffer, true)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(stickers.count)) + for item in stickers { + item.serialize(buffer, true) + } + if Int(flags) & Int(1 << 3) != 0 {serializeString(software!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "stickers.createStickerSet", parameters: [("flags", String(describing: flags)), ("userId", String(describing: userId)), ("title", String(describing: title)), ("shortName", String(describing: shortName)), ("thumb", String(describing: thumb)), ("stickers", String(describing: stickers)), ("software", String(describing: software))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in + let reader = BufferReader(buffer) + var result: Api.messages.StickerSet? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.StickerSet + } + return result + }) + } +} +public extension Api.functions.stickers { + static func deleteStickerSet(stickerset: Api.InputStickerSet) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-2022685804) + stickerset.serialize(buffer, true) + return (FunctionDescription(name: "stickers.deleteStickerSet", parameters: [("stickerset", String(describing: stickerset))]), 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.stickers { + static func removeStickerFromSet(sticker: Api.InputDocument) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-143257775) + sticker.serialize(buffer, true) + return (FunctionDescription(name: "stickers.removeStickerFromSet", parameters: [("sticker", String(describing: sticker))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in + let reader = BufferReader(buffer) + var result: Api.messages.StickerSet? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.StickerSet + } + return result + }) + } +} +public extension Api.functions.stickers { + static func renameStickerSet(stickerset: Api.InputStickerSet, title: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(306912256) + stickerset.serialize(buffer, true) + serializeString(title, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stickers.renameStickerSet", parameters: [("stickerset", String(describing: stickerset)), ("title", String(describing: title))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in + let reader = BufferReader(buffer) + var result: Api.messages.StickerSet? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.StickerSet + } + return result + }) + } +} +public extension Api.functions.stickers { + static func setStickerSetThumb(flags: Int32, stickerset: Api.InputStickerSet, thumb: Api.InputDocument?, thumbDocumentId: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1486204014) + serializeInt32(flags, buffer: buffer, boxed: false) + stickerset.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {thumb!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt64(thumbDocumentId!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "stickers.setStickerSetThumb", parameters: [("flags", String(describing: flags)), ("stickerset", String(describing: stickerset)), ("thumb", String(describing: thumb)), ("thumbDocumentId", String(describing: thumbDocumentId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in + let reader = BufferReader(buffer) + var result: Api.messages.StickerSet? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.StickerSet + } + return result + }) + } +} +public extension Api.functions.stickers { + static func suggestShortName(title: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1303364867) + serializeString(title, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stickers.suggestShortName", parameters: [("title", String(describing: title))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stickers.SuggestedShortName? in + let reader = BufferReader(buffer) + var result: Api.stickers.SuggestedShortName? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.stickers.SuggestedShortName + } + return result + }) + } +} +public extension Api.functions.stories { + static func activateStealthMode(flags: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1471926630) + serializeInt32(flags, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stories.activateStealthMode", parameters: [("flags", String(describing: flags))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.stories { + static func canSendStory(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-941629475) + peer.serialize(buffer, true) + return (FunctionDescription(name: "stories.canSendStory", parameters: [("peer", String(describing: peer))]), 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.stories { + static func deleteStories(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int32]>) { + let buffer = Buffer() + buffer.appendInt32(-1369842849) + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "stories.deleteStories", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int32]? in + let reader = BufferReader(buffer) + var result: [Int32]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + return result + }) + } +} +public extension Api.functions.stories { + static func editStory(flags: Int32, peer: Api.InputPeer, id: Int32, media: Api.InputMedia?, mediaAreas: [Api.MediaArea]?, caption: String?, entities: [Api.MessageEntity]?, privacyRules: [Api.InputPrivacyRule]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1249658298) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {media!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(mediaAreas!.count)) + for item in mediaAreas! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 1) != 0 {serializeString(caption!, 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 << 2) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(privacyRules!.count)) + for item in privacyRules! { + item.serialize(buffer, true) + }} + return (FunctionDescription(name: "stories.editStory", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id)), ("media", String(describing: media)), ("mediaAreas", String(describing: mediaAreas)), ("caption", String(describing: caption)), ("entities", String(describing: entities)), ("privacyRules", String(describing: privacyRules))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.stories { + static func exportStoryLink(peer: Api.InputPeer, id: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2072899360) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stories.exportStoryLink", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedStoryLink? in + let reader = BufferReader(buffer) + var result: Api.ExportedStoryLink? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.ExportedStoryLink + } + return result + }) + } +} +public extension Api.functions.stories { + static func getAllReadPeerStories() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1688541191) + + return (FunctionDescription(name: "stories.getAllReadPeerStories", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.stories { + static func getAllStories(flags: Int32, state: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-290400731) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(state!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "stories.getAllStories", parameters: [("flags", String(describing: flags)), ("state", String(describing: state))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.AllStories? in + let reader = BufferReader(buffer) + var result: Api.stories.AllStories? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.stories.AllStories + } + return result + }) + } +} +public extension Api.functions.stories { + static func getChatsToSend() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1519744160) + + return (FunctionDescription(name: "stories.getChatsToSend", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Chats? in + let reader = BufferReader(buffer) + var result: Api.messages.Chats? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Chats + } + return result + }) + } +} +public extension Api.functions.stories { + static func getPeerMaxIDs(id: [Api.InputPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int32]>) { + let buffer = Buffer() + buffer.appendInt32(1398375363) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "stories.getPeerMaxIDs", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int32]? in + let reader = BufferReader(buffer) + var result: [Int32]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + return result + }) + } +} +public extension Api.functions.stories { + static func getPeerStories(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(743103056) + peer.serialize(buffer, true) + return (FunctionDescription(name: "stories.getPeerStories", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.PeerStories? in + let reader = BufferReader(buffer) + var result: Api.stories.PeerStories? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.stories.PeerStories + } + return result + }) + } +} +public extension Api.functions.stories { + static func getPinnedStories(peer: Api.InputPeer, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1478600156) + peer.serialize(buffer, true) + serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stories.getPinnedStories", parameters: [("peer", String(describing: peer)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.Stories? in + let reader = BufferReader(buffer) + var result: Api.stories.Stories? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.stories.Stories + } + return result + }) + } +} +public extension Api.functions.stories { + static func getStoriesArchive(peer: Api.InputPeer, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1271586794) + peer.serialize(buffer, true) + serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stories.getStoriesArchive", parameters: [("peer", String(describing: peer)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.Stories? in + let reader = BufferReader(buffer) + var result: Api.stories.Stories? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.stories.Stories + } + return result + }) + } +} +public extension Api.functions.stories { + static func getStoriesByID(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1467271796) + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "stories.getStoriesByID", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.Stories? in + let reader = BufferReader(buffer) + var result: Api.stories.Stories? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.stories.Stories + } + return result + }) + } +} +public extension Api.functions.stories { + static func getStoriesViews(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(685862088) + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "stories.getStoriesViews", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.StoryViews? in + let reader = BufferReader(buffer) + var result: Api.stories.StoryViews? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.stories.StoryViews + } + return result + }) + } +} +public extension Api.functions.stories { + static func getStoryViewsList(flags: Int32, peer: Api.InputPeer, q: String?, id: Int32, offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2127707223) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 1) != 0 {serializeString(q!, buffer: buffer, boxed: false)} + serializeInt32(id, buffer: buffer, boxed: false) + serializeString(offset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stories.getStoryViewsList", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("q", String(describing: q)), ("id", String(describing: id)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.StoryViewsList? in + let reader = BufferReader(buffer) + var result: Api.stories.StoryViewsList? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.stories.StoryViewsList + } + return result + }) + } +} +public extension Api.functions.stories { + static func incrementStoryViews(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1308456197) + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "stories.incrementStoryViews", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), 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.stories { + static func readStories(peer: Api.InputPeer, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int32]>) { + let buffer = Buffer() + buffer.appendInt32(-1521034552) + peer.serialize(buffer, true) + serializeInt32(maxId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stories.readStories", parameters: [("peer", String(describing: peer)), ("maxId", String(describing: maxId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int32]? in + let reader = BufferReader(buffer) + var result: [Int32]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + return result + }) + } +} +public extension Api.functions.stories { + static func report(peer: Api.InputPeer, id: [Int32], reason: Api.ReportReason, message: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(421788300) + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + reason.serialize(buffer, true) + serializeString(message, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stories.report", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("reason", String(describing: reason)), ("message", String(describing: message))]), 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.stories { + static func sendReaction(flags: Int32, peer: Api.InputPeer, storyId: Int32, reaction: Api.Reaction) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2144810674) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(storyId, buffer: buffer, boxed: false) + reaction.serialize(buffer, true) + return (FunctionDescription(name: "stories.sendReaction", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("storyId", String(describing: storyId)), ("reaction", String(describing: reaction))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.stories { + static func sendStory(flags: Int32, peer: Api.InputPeer, media: Api.InputMedia, mediaAreas: [Api.MediaArea]?, caption: String?, entities: [Api.MessageEntity]?, privacyRules: [Api.InputPrivacyRule], randomId: Int64, period: Int32?, fwdFromId: Api.InputPeer?, fwdFromStory: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-454661813) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + media.serialize(buffer, true) + if Int(flags) & Int(1 << 5) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(mediaAreas!.count)) + for item in mediaAreas! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 0) != 0 {serializeString(caption!, 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) + }} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(privacyRules.count)) + for item in privacyRules { + item.serialize(buffer, true) + } + serializeInt64(randomId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 3) != 0 {serializeInt32(period!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 6) != 0 {fwdFromId!.serialize(buffer, true)} + if Int(flags) & Int(1 << 6) != 0 {serializeInt32(fwdFromStory!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "stories.sendStory", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("media", String(describing: media)), ("mediaAreas", String(describing: mediaAreas)), ("caption", String(describing: caption)), ("entities", String(describing: entities)), ("privacyRules", String(describing: privacyRules)), ("randomId", String(describing: randomId)), ("period", String(describing: period)), ("fwdFromId", String(describing: fwdFromId)), ("fwdFromStory", String(describing: fwdFromStory))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.stories { + static func toggleAllStoriesHidden(hidden: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2082822084) + hidden.serialize(buffer, true) + return (FunctionDescription(name: "stories.toggleAllStoriesHidden", parameters: [("hidden", String(describing: hidden))]), 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.stories { + static func togglePeerStoriesHidden(peer: Api.InputPeer, hidden: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1123805756) + peer.serialize(buffer, true) + hidden.serialize(buffer, true) + return (FunctionDescription(name: "stories.togglePeerStoriesHidden", parameters: [("peer", String(describing: peer)), ("hidden", String(describing: hidden))]), 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.stories { + static func togglePinned(peer: Api.InputPeer, id: [Int32], pinned: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int32]>) { + let buffer = Buffer() + buffer.appendInt32(-1703566865) + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + pinned.serialize(buffer, true) + return (FunctionDescription(name: "stories.togglePinned", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("pinned", String(describing: pinned))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int32]? in + let reader = BufferReader(buffer) + var result: [Int32]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + return result + }) + } +} +public extension Api.functions.updates { + static func getChannelDifference(flags: Int32, channel: Api.InputChannel, filter: Api.ChannelMessagesFilter, pts: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(51854712) + serializeInt32(flags, buffer: buffer, boxed: false) + channel.serialize(buffer, true) + filter.serialize(buffer, true) + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "updates.getChannelDifference", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("filter", String(describing: filter)), ("pts", String(describing: pts)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.updates.ChannelDifference? in + let reader = BufferReader(buffer) + var result: Api.updates.ChannelDifference? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.updates.ChannelDifference + } + return result + }) + } +} +public extension Api.functions.updates { + static func getDifference(flags: Int32, pts: Int32, ptsLimit: Int32?, ptsTotalLimit: Int32?, date: Int32, qts: Int32, qtsLimit: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(432207715) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(pts, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(ptsLimit!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(ptsTotalLimit!, buffer: buffer, boxed: false)} + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt32(qts, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {serializeInt32(qtsLimit!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "updates.getDifference", parameters: [("flags", String(describing: flags)), ("pts", String(describing: pts)), ("ptsLimit", String(describing: ptsLimit)), ("ptsTotalLimit", String(describing: ptsTotalLimit)), ("date", String(describing: date)), ("qts", String(describing: qts)), ("qtsLimit", String(describing: qtsLimit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.updates.Difference? in + let reader = BufferReader(buffer) + var result: Api.updates.Difference? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.updates.Difference + } + return result + }) + } +} +public extension Api.functions.updates { + static func getState() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-304838614) + + return (FunctionDescription(name: "updates.getState", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.updates.State? in + let reader = BufferReader(buffer) + var result: Api.updates.State? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.updates.State + } + return result + }) + } +} +public extension Api.functions.upload { + static func getCdnFile(fileToken: Buffer, offset: Int64, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(962554330) + serializeBytes(fileToken, buffer: buffer, boxed: false) + serializeInt64(offset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "upload.getCdnFile", parameters: [("fileToken", String(describing: fileToken)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.upload.CdnFile? in + let reader = BufferReader(buffer) + var result: Api.upload.CdnFile? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.upload.CdnFile + } + return result + }) + } +} +public extension Api.functions.upload { + static func getCdnFileHashes(fileToken: Buffer, offset: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.FileHash]>) { + let buffer = Buffer() + buffer.appendInt32(-1847836879) + serializeBytes(fileToken, buffer: buffer, boxed: false) + serializeInt64(offset, buffer: buffer, boxed: false) + return (FunctionDescription(name: "upload.getCdnFileHashes", parameters: [("fileToken", String(describing: fileToken)), ("offset", String(describing: offset))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.FileHash]? in + let reader = BufferReader(buffer) + var result: [Api.FileHash]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.FileHash.self) + } + return result + }) + } +} +public extension Api.functions.upload { + static func getFile(flags: Int32, location: Api.InputFileLocation, offset: Int64, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1101843010) + serializeInt32(flags, buffer: buffer, boxed: false) + location.serialize(buffer, true) + serializeInt64(offset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "upload.getFile", parameters: [("flags", String(describing: flags)), ("location", String(describing: location)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.upload.File? in + let reader = BufferReader(buffer) + var result: Api.upload.File? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.upload.File + } + return result + }) + } +} +public extension Api.functions.upload { + static func getFileHashes(location: Api.InputFileLocation, offset: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.FileHash]>) { + let buffer = Buffer() + buffer.appendInt32(-1856595926) + location.serialize(buffer, true) + serializeInt64(offset, buffer: buffer, boxed: false) + return (FunctionDescription(name: "upload.getFileHashes", parameters: [("location", String(describing: location)), ("offset", String(describing: offset))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.FileHash]? in + let reader = BufferReader(buffer) + var result: [Api.FileHash]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.FileHash.self) + } + return result + }) + } +} +public extension Api.functions.upload { + static func getWebFile(location: Api.InputWebFileLocation, offset: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(619086221) + location.serialize(buffer, true) + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "upload.getWebFile", parameters: [("location", String(describing: location)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.upload.WebFile? in + let reader = BufferReader(buffer) + var result: Api.upload.WebFile? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.upload.WebFile + } + return result + }) + } +} +public extension Api.functions.upload { + static func reuploadCdnFile(fileToken: Buffer, requestToken: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.FileHash]>) { + let buffer = Buffer() + buffer.appendInt32(-1691921240) + serializeBytes(fileToken, buffer: buffer, boxed: false) + serializeBytes(requestToken, buffer: buffer, boxed: false) + return (FunctionDescription(name: "upload.reuploadCdnFile", parameters: [("fileToken", String(describing: fileToken)), ("requestToken", String(describing: requestToken))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.FileHash]? in + let reader = BufferReader(buffer) + var result: [Api.FileHash]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.FileHash.self) + } + return result + }) + } +} +public extension Api.functions.upload { + static func saveBigFilePart(fileId: Int64, filePart: Int32, fileTotalParts: Int32, bytes: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-562337987) + serializeInt64(fileId, buffer: buffer, boxed: false) + serializeInt32(filePart, buffer: buffer, boxed: false) + serializeInt32(fileTotalParts, buffer: buffer, boxed: false) + serializeBytes(bytes, buffer: buffer, boxed: false) + return (FunctionDescription(name: "upload.saveBigFilePart", parameters: [("fileId", String(describing: fileId)), ("filePart", String(describing: filePart)), ("fileTotalParts", String(describing: fileTotalParts)), ("bytes", String(describing: bytes))]), 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.upload { + static func saveFilePart(fileId: Int64, filePart: Int32, bytes: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1291540959) + serializeInt64(fileId, buffer: buffer, boxed: false) + serializeInt32(filePart, buffer: buffer, boxed: false) + serializeBytes(bytes, buffer: buffer, boxed: false) + return (FunctionDescription(name: "upload.saveFilePart", parameters: [("fileId", String(describing: fileId)), ("filePart", String(describing: filePart)), ("bytes", String(describing: bytes))]), 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.users { + static func getFullUser(id: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1240508136) + id.serialize(buffer, true) + return (FunctionDescription(name: "users.getFullUser", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.users.UserFull? in + let reader = BufferReader(buffer) + var result: Api.users.UserFull? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.users.UserFull + } + return result + }) + } +} +public extension Api.functions.users { + static func getUsers(id: [Api.InputUser]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.User]>) { + let buffer = Buffer() + buffer.appendInt32(227648840) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "users.getUsers", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.User]? in + let reader = BufferReader(buffer) + var result: [Api.User]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + return result + }) + } +} +public extension Api.functions.users { + static func setSecureValueErrors(id: Api.InputUser, errors: [Api.SecureValueError]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1865902923) + id.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(errors.count)) + for item in errors { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "users.setSecureValueErrors", parameters: [("id", String(describing: id)), ("errors", String(describing: errors))]), 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 + }) + } +} diff --git a/submodules/TelegramCallsUI/BUILD b/submodules/TelegramCallsUI/BUILD index dbd5bc87948..7193bd11f75 100644 --- a/submodules/TelegramCallsUI/BUILD +++ b/submodules/TelegramCallsUI/BUILD @@ -105,6 +105,11 @@ swift_library( "//submodules/Components/HierarchyTrackingLayer:HierarchyTrackingLayer", "//submodules/PeerInfoUI/CreateExternalMediaStreamScreen:CreateExternalMediaStreamScreen", "//submodules/PhoneNumberFormat:PhoneNumberFormat", + "//submodules/TelegramUI/Components/Calls/CallScreen", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/TinyThumbnail", + "//submodules/ImageBlur", + "//submodules/MetalEngine", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramCallsUI/CallsEmoji/PublicHeaders/CallsEmoji/CallsEmoji.h b/submodules/TelegramCallsUI/CallsEmoji/PublicHeaders/CallsEmoji/CallsEmoji.h index d4ba4057432..0459c61cd08 100644 --- a/submodules/TelegramCallsUI/CallsEmoji/PublicHeaders/CallsEmoji/CallsEmoji.h +++ b/submodules/TelegramCallsUI/CallsEmoji/PublicHeaders/CallsEmoji/CallsEmoji.h @@ -4,6 +4,6 @@ #import NSString *randomCallsEmoji(); -NSString *stringForEmojiHashOfData(NSData *data, NSInteger count); +NSArray *stringForEmojiHashOfData(NSData *data, NSInteger count); #endif /* CallsEmoji_h */ diff --git a/submodules/TelegramCallsUI/CallsEmoji/Sources/CallsEmoji.m b/submodules/TelegramCallsUI/CallsEmoji/Sources/CallsEmoji.m index 5f5f29ac441..890042cd4fa 100644 --- a/submodules/TelegramCallsUI/CallsEmoji/Sources/CallsEmoji.m +++ b/submodules/TelegramCallsUI/CallsEmoji/Sources/CallsEmoji.m @@ -16,20 +16,21 @@ static int32_t positionExtractor(uint8_t *bytes, int32_t i, int32_t count) { return emojis[arc4random() % emojis.count]; } -NSString *stringForEmojiHashOfData(NSData *data, NSInteger count) { - if (data.length != 32) - return @""; +NSArray *stringForEmojiHashOfData(NSData *data, NSInteger count) { + if (data.length != 32) { + return @[]; + } uint8_t bytes[32]; [data getBytes:bytes length:32]; NSArray *emojis = emojisArray(); - NSString *result = @""; + NSMutableArray *result = [[NSMutableArray alloc] init]; for (int32_t i = 0; i < count; i++) { int32_t position = positionExtractor(bytes, i, (int32_t)emojis.count); NSString *emoji = emojis[position]; - result = [result stringByAppendingString:emoji]; + [result addObject:emoji]; } return result; diff --git a/submodules/TelegramCallsUI/Sources/CallController.swift b/submodules/TelegramCallsUI/Sources/CallController.swift index 292b14952fe..0e096d91bb2 100644 --- a/submodules/TelegramCallsUI/Sources/CallController.swift +++ b/submodules/TelegramCallsUI/Sources/CallController.swift @@ -135,10 +135,10 @@ public final class CallController: ViewController { } override public func loadDisplayNode() { - if self.call.isVideoPossible { - self.displayNode = CallControllerNode(sharedContext: self.sharedContext, account: self.account, presentationData: self.presentationData, statusBar: self.statusBar, debugInfo: self.call.debugInfo(), shouldStayHiddenUntilConnection: !self.call.isOutgoing && self.call.isIntegratedWithCallKit, easyDebugAccess: self.easyDebugAccess, call: self.call) + if self.sharedContext.immediateExperimentalUISettings.callUIV2 { + self.displayNode = CallControllerNodeV2(sharedContext: self.sharedContext, account: self.account, presentationData: self.presentationData, statusBar: self.statusBar, debugInfo: self.call.debugInfo(), shouldStayHiddenUntilConnection: !self.call.isOutgoing && self.call.isIntegratedWithCallKit, easyDebugAccess: self.easyDebugAccess, call: self.call) } else { - self.displayNode = LegacyCallControllerNode(sharedContext: self.sharedContext, account: self.account, presentationData: self.presentationData, statusBar: self.statusBar, debugInfo: self.call.debugInfo(), shouldStayHiddenUntilConnection: !self.call.isOutgoing && self.call.isIntegratedWithCallKit, easyDebugAccess: self.easyDebugAccess, call: self.call) + self.displayNode = CallControllerNode(sharedContext: self.sharedContext, account: self.account, presentationData: self.presentationData, statusBar: self.statusBar, debugInfo: self.call.debugInfo(), shouldStayHiddenUntilConnection: !self.call.isOutgoing && self.call.isIntegratedWithCallKit, easyDebugAccess: self.easyDebugAccess, call: self.call) } self.displayNodeDidLoad() diff --git a/submodules/TelegramCallsUI/Sources/CallControllerNode.swift b/submodules/TelegramCallsUI/Sources/CallControllerNode.swift index 685545c5d9f..d6f9fb13aba 100644 --- a/submodules/TelegramCallsUI/Sources/CallControllerNode.swift +++ b/submodules/TelegramCallsUI/Sources/CallControllerNode.swift @@ -1105,7 +1105,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro isReconnecting = true } if self.keyTextData?.0 != keyVisualHash { - let text = stringForEmojiHashOfData(keyVisualHash, 4)! + let text = stringForEmojiHashOfData(keyVisualHash, 4)!.joined(separator: "") self.keyTextData = (keyVisualHash, text) self.keyButtonNode.key = text diff --git a/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift b/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift new file mode 100644 index 00000000000..12c772d8b0c --- /dev/null +++ b/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift @@ -0,0 +1,447 @@ +import Foundation +import AsyncDisplayKit +import Display +import TelegramCore +import Postbox +import TelegramAudio +import AccountContext +import TelegramPresentationData +import SwiftSignalKit +import CallScreen +import ComponentDisplayAdapters +import ComponentFlow +import CallsEmoji +import AvatarNode +import TinyThumbnail +import ImageBlur +import TelegramVoip +import MetalEngine + +final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeProtocol { + private let sharedContext: SharedAccountContext + private let account: Account + private let presentationData: PresentationData + private let statusBar: StatusBar + private let call: PresentationCall + + private let containerView: UIView + private let callScreen: PrivateCallScreen + private var callScreenState: PrivateCallScreen.State? + + private var shouldStayHiddenUntilConnection: Bool = false + + private var callStartTimestamp: Double? + + var isMuted: Bool = false + + var toggleMute: (() -> Void)? + var setCurrentAudioOutput: ((AudioSessionOutput) -> Void)? + var beginAudioOuputSelection: ((Bool) -> Void)? + var acceptCall: (() -> Void)? + var endCall: (() -> Void)? + var back: (() -> Void)? + var presentCallRating: ((CallId, Bool) -> Void)? + var present: ((ViewController) -> Void)? + var callEnded: ((Bool) -> Void)? + var dismissedInteractively: (() -> Void)? + var dismissAllTooltips: (() -> Void)? + + private var emojiKey: (data: Data, resolvedKey: [String])? + private var validLayout: (layout: ContainerViewLayout, navigationBarHeight: CGFloat)? + + private var currentPeer: EnginePeer? + private var peerAvatarDisposable: Disposable? + + private var availableAudioOutputs: [AudioSessionOutput]? + private var isMicrophoneMutedDisposable: Disposable? + private var audioLevelDisposable: Disposable? + + private var remoteVideo: AdaptedCallVideoSource? + + init( + sharedContext: SharedAccountContext, + account: Account, + presentationData: PresentationData, + statusBar: StatusBar, + debugInfo: Signal<(String, String), NoError>, + shouldStayHiddenUntilConnection: Bool = false, + easyDebugAccess: Bool, + call: PresentationCall + ) { + self.sharedContext = sharedContext + self.account = account + self.presentationData = presentationData + self.statusBar = statusBar + self.call = call + + self.containerView = UIView() + self.callScreen = PrivateCallScreen() + + self.shouldStayHiddenUntilConnection = shouldStayHiddenUntilConnection + + super.init() + + self.view.addSubview(self.containerView) + self.containerView.addSubview(self.callScreen) + + self.callScreen.speakerAction = { [weak self] in + guard let self else { + return + } + self.beginAudioOuputSelection?(false) + } + self.callScreen.videoAction = { [weak self] in + guard let self else { + return + } + let _ = self + } + self.callScreen.microhoneMuteAction = { [weak self] in + guard let self else { + return + } + self.call.toggleIsMuted() + } + self.callScreen.endCallAction = { [weak self] in + guard let self else { + return + } + self.endCall?() + } + + self.callScreenState = PrivateCallScreen.State( + lifecycleState: .connecting, + name: " ", + avatarImage: nil, + audioOutput: .internalSpeaker, + isMicrophoneMuted: false, + localVideo: nil, + remoteVideo: nil + ) + if let peer = call.peer { + self.updatePeer(peer: peer) + } + + self.isMicrophoneMutedDisposable = (call.isMuted + |> deliverOnMainQueue).startStrict(next: { [weak self] isMuted in + guard let self, var callScreenState = self.callScreenState else { + return + } + self.isMuted = isMuted + if callScreenState.isMicrophoneMuted != isMuted { + callScreenState.isMicrophoneMuted = isMuted + self.callScreenState = callScreenState + self.update(transition: .animated(duration: 0.3, curve: .spring)) + } + }) + + self.audioLevelDisposable = (call.audioLevel + |> deliverOnMainQueue).start(next: { [weak self] audioLevel in + guard let self else { + return + } + self.callScreen.addIncomingAudioLevel(value: audioLevel) + }) + } + + deinit { + self.peerAvatarDisposable?.dispose() + self.isMicrophoneMutedDisposable?.dispose() + self.audioLevelDisposable?.dispose() + } + + func updateAudioOutputs(availableOutputs: [AudioSessionOutput], currentOutput: AudioSessionOutput?) { + self.availableAudioOutputs = availableOutputs + + if var callScreenState = self.callScreenState { + let mappedOutput: PrivateCallScreen.State.AudioOutput + if let currentOutput { + switch currentOutput { + case .builtin: + mappedOutput = .internalSpeaker + case .speaker: + mappedOutput = .speaker + case .headphones, .port: + mappedOutput = .speaker + } + } else { + mappedOutput = .internalSpeaker + } + + if callScreenState.audioOutput != mappedOutput { + callScreenState.audioOutput = mappedOutput + self.callScreenState = callScreenState + self.update(transition: .animated(duration: 0.3, curve: .spring)) + } + } + } + + private func resolvedEmojiKey(data: Data) -> [String] { + if let emojiKey = self.emojiKey, emojiKey.data == data { + return emojiKey.resolvedKey + } + let resolvedKey = stringForEmojiHashOfData(data, 4) ?? [] + self.emojiKey = (data, resolvedKey) + return resolvedKey + } + + func updateCallState(_ callState: PresentationCallState) { + let mappedLifecycleState: PrivateCallScreen.State.LifecycleState + switch callState.state { + case .waiting: + mappedLifecycleState = .connecting + case .ringing: + mappedLifecycleState = .ringing + case let .requesting(isRinging): + if isRinging { + mappedLifecycleState = .ringing + } else { + mappedLifecycleState = .connecting + } + case let .connecting(keyData): + let _ = keyData + mappedLifecycleState = .exchangingKeys + case let .active(startTime, signalQuality, keyData): + self.callStartTimestamp = startTime + + let _ = keyData + mappedLifecycleState = .active(PrivateCallScreen.State.ActiveState( + startTime: startTime + kCFAbsoluteTimeIntervalSince1970, + signalInfo: PrivateCallScreen.State.SignalInfo(quality: Double(signalQuality ?? 0) / 4.0), + emojiKey: self.resolvedEmojiKey(data: keyData) + )) + case let .reconnecting(startTime, _, keyData): + let _ = keyData + mappedLifecycleState = .active(PrivateCallScreen.State.ActiveState( + startTime: startTime + kCFAbsoluteTimeIntervalSince1970, + signalInfo: PrivateCallScreen.State.SignalInfo(quality: 0.0), + emojiKey: self.resolvedEmojiKey(data: keyData) + )) + case .terminating, .terminated: + let duration: Double + if let callStartTimestamp = self.callStartTimestamp { + duration = CFAbsoluteTimeGetCurrent() - callStartTimestamp + } else { + duration = 0.0 + } + mappedLifecycleState = .terminated(PrivateCallScreen.State.TerminatedState(duration: duration)) + } + + switch callState.remoteVideoState { + case .active, .paused: + if self.remoteVideo == nil, let call = self.call as? PresentationCallImpl, let videoStreamSignal = call.video(isIncoming: true) { + self.remoteVideo = AdaptedCallVideoSource(videoStreamSignal: videoStreamSignal) + } + case .inactive: + self.remoteVideo = nil + } + + if var callScreenState = self.callScreenState { + callScreenState.lifecycleState = mappedLifecycleState + callScreenState.remoteVideo = self.remoteVideo + + if self.callScreenState != callScreenState { + self.callScreenState = callScreenState + self.update(transition: .animated(duration: 0.35, curve: .spring)) + } + } + + if case let .terminated(_, _, reportRating) = callState.state { + self.callEnded?(reportRating) + } + } + + func updatePeer(accountPeer: Peer, peer: Peer, hasOther: Bool) { + self.updatePeer(peer: EnginePeer(peer)) + } + + private func updatePeer(peer: EnginePeer) { + guard var callScreenState = self.callScreenState else { + return + } + callScreenState.name = peer.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) + + if self.currentPeer?.smallProfileImage != peer.smallProfileImage { + self.peerAvatarDisposable?.dispose() + + if let smallProfileImage = peer.largeProfileImage, let peerReference = PeerReference(peer._asPeer()) { + if let thumbnailImage = smallProfileImage.immediateThumbnailData.flatMap(decodeTinyThumbnail).flatMap(UIImage.init(data:)), let cgImage = thumbnailImage.cgImage { + callScreenState.avatarImage = generateImage(CGSize(width: 128.0, height: 128.0), contextGenerator: { size, context in + context.draw(cgImage, in: CGRect(origin: CGPoint(), size: size)) + }, scale: 1.0).flatMap { image in + return blurredImage(image, radius: 10.0) + } + } + + let postbox = self.call.context.account.postbox + self.peerAvatarDisposable = (Signal { subscriber in + let fetchDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: .other, userContentType: .avatar, reference: .avatar(peer: peerReference, resource: smallProfileImage.resource)).start() + let dataDisposable = postbox.mediaBox.resourceData(smallProfileImage.resource).start(next: { data in + if data.complete, let image = UIImage(contentsOfFile: data.path)?.precomposed() { + subscriber.putNext(image) + subscriber.putCompletion() + } + }) + + return ActionDisposable { + fetchDisposable.dispose() + dataDisposable.dispose() + } + } + |> deliverOnMainQueue).start(next: { [weak self] image in + guard let self else { + return + } + if var callScreenState = self.callScreenState { + callScreenState.avatarImage = image + self.callScreenState = callScreenState + self.update(transition: .immediate) + } + }) + } else { + self.peerAvatarDisposable?.dispose() + self.peerAvatarDisposable = nil + + callScreenState.avatarImage = generateImage(CGSize(width: 512, height: 512), scale: 1.0, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + drawPeerAvatarLetters(context: context, size: size, font: Font.semibold(20.0), letters: peer.displayLetters, peerId: peer.id, nameColor: peer.nameColor) + }) + } + } + self.currentPeer = peer + + if callScreenState != self.callScreenState { + self.callScreenState = callScreenState + self.update(transition: .immediate) + } + } + + func animateIn() { + if !self.containerView.alpha.isZero { + var bounds = self.bounds + bounds.origin = CGPoint() + self.bounds = bounds + self.layer.removeAnimation(forKey: "bounds") + self.statusBar.layer.removeAnimation(forKey: "opacity") + self.containerView.layer.removeAnimation(forKey: "opacity") + self.containerView.layer.removeAnimation(forKey: "scale") + self.statusBar.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + + if !self.shouldStayHiddenUntilConnection { + self.containerView.layer.animateScale(from: 1.04, to: 1.0, duration: 0.3) + self.containerView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + } + } + + func animateOut(completion: @escaping () -> Void) { + self.statusBar.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) + if !self.shouldStayHiddenUntilConnection || self.containerView.alpha > 0.0 { + self.containerView.layer.allowsGroupOpacity = true + self.containerView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak self] _ in + self?.containerView.layer.allowsGroupOpacity = false + }) + self.containerView.layer.animateScale(from: 1.0, to: 1.04, duration: 0.3, removeOnCompletion: false, completion: { _ in + completion() + }) + } else { + completion() + } + } + + func expandFromPipIfPossible() { + + } + + private func update(transition: ContainedViewLayoutTransition) { + guard let (layout, navigationBarHeight) = self.validLayout else { + return + } + self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition) + } + + func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { + self.validLayout = (layout, navigationBarHeight) + + transition.updateFrame(view: self.containerView, frame: CGRect(origin: CGPoint(), size: layout.size)) + transition.updateFrame(view: self.callScreen, frame: CGRect(origin: CGPoint(), size: layout.size)) + + if let callScreenState = self.callScreenState { + self.callScreen.update( + size: layout.size, + insets: layout.insets(options: [.statusBar]), + screenCornerRadius: layout.deviceMetrics.screenCornerRadius, + state: callScreenState, + transition: Transition(transition) + ) + } + } +} + +private final class AdaptedCallVideoSource: VideoSource { + private static let queue = Queue(name: "AdaptedCallVideoSource") + var updated: (() -> Void)? + private(set) var currentOutput: Output? + + private var textureCache: CVMetalTextureCache? + private var videoFrameDisposable: Disposable? + + init(videoStreamSignal: Signal) { + CVMetalTextureCacheCreate(nil, nil, MetalEngine.shared.device, nil, &self.textureCache) + + self.videoFrameDisposable = (videoStreamSignal + |> deliverOnMainQueue).start(next: { [weak self] videoFrameData in + guard let self, let textureCache = self.textureCache else { + return + } + + let rotationAngle: Float + switch videoFrameData.orientation { + case .rotation0: + rotationAngle = 0.0 + case .rotation90: + rotationAngle = Float.pi * 0.5 + case .rotation180: + rotationAngle = Float.pi + case .rotation270: + rotationAngle = Float.pi * 3.0 / 2.0 + } + + AdaptedCallVideoSource.queue.async { [weak self] in + let output: Output + switch videoFrameData.buffer { + case let .native(nativeBuffer): + let width = CVPixelBufferGetWidth(nativeBuffer.pixelBuffer) + let height = CVPixelBufferGetHeight(nativeBuffer.pixelBuffer) + + var cvMetalTextureY: CVMetalTexture? + var status = CVMetalTextureCacheCreateTextureFromImage(nil, textureCache, nativeBuffer.pixelBuffer, nil, .r8Unorm, width, height, 0, &cvMetalTextureY) + guard status == kCVReturnSuccess, let yTexture = CVMetalTextureGetTexture(cvMetalTextureY!) else { + return + } + var cvMetalTextureUV: CVMetalTexture? + status = CVMetalTextureCacheCreateTextureFromImage(nil, textureCache, nativeBuffer.pixelBuffer, nil, .rg8Unorm, width / 2, height / 2, 1, &cvMetalTextureUV) + guard status == kCVReturnSuccess, let uvTexture = CVMetalTextureGetTexture(cvMetalTextureUV!) else { + return + } + + output = Output(y: yTexture, uv: uvTexture, rotationAngle: rotationAngle, sourceId: videoFrameData.mirrorHorizontally || videoFrameData.mirrorVertically ? 1 : 0) + default: + return + } + + DispatchQueue.main.async { + guard let self else { + return + } + self.currentOutput = output + self.updated?() + } + } + }) + } + + deinit { + self.videoFrameDisposable?.dispose() + } +} diff --git a/submodules/TelegramCallsUI/Sources/LegacyCallControllerNode.swift b/submodules/TelegramCallsUI/Sources/LegacyCallControllerNode.swift index 3d69c388330..894fcf94d9e 100644 --- a/submodules/TelegramCallsUI/Sources/LegacyCallControllerNode.swift +++ b/submodules/TelegramCallsUI/Sources/LegacyCallControllerNode.swift @@ -253,7 +253,7 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol } }, timestamp) if self.keyTextData?.0 != keyVisualHash { - let text = stringForEmojiHashOfData(keyVisualHash, 4)! + let text = stringForEmojiHashOfData(keyVisualHash, 4)!.joined(separator: "") self.keyTextData = (keyVisualHash, text) self.keyButtonNode.setAttributedTitle(NSAttributedString(string: text, attributes: [NSAttributedString.Key.font: Font.regular(22.0), NSAttributedString.Key.kern: 2.5 as NSNumber]), for: []) diff --git a/submodules/TelegramCore/Sources/Account/Account.swift b/submodules/TelegramCore/Sources/Account/Account.swift index fba5dcc0c10..a298b0007ad 100644 --- a/submodules/TelegramCore/Sources/Account/Account.swift +++ b/submodules/TelegramCore/Sources/Account/Account.swift @@ -1591,7 +1591,7 @@ public func standaloneStateManager( Logger.shared.log("StandaloneStateManager", "received network") postbox.mediaBox.fetchResource = { [weak postbox] resource, intervals, parameters -> Signal in - guard let postbox else { + guard let postbox = postbox else { return .never() } if let result = auxiliaryMethods.fetchResource( diff --git a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift index 207923e6111..086807c5a6a 100644 --- a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift +++ b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift @@ -125,6 +125,7 @@ enum AccountStateMutationOperation { case UpdateStoryStealthMode(data: Api.StoriesStealthMode) case UpdateStorySentReaction(peerId: PeerId, id: Int32, reaction: Api.Reaction) case UpdateNewAuthorization(isUnconfirmed: Bool, hash: Int64, date: Int32, device: String, location: String) + case UpdateWallpaper(peerId: PeerId, wallpaper: TelegramWallpaper?) } struct HoleFromPreviousState { @@ -468,7 +469,7 @@ struct AccountMutableState { for chat in chats { switch chat { - case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _): + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _): if let participantsCount = participantsCount { self.addOperation(.UpdateCachedPeerData(chat.peerId, { current in var previous: CachedChannelData @@ -522,6 +523,10 @@ struct AccountMutableState { self.addOperation(.UpdateTheme(theme)) } + mutating func updateWallpaper(peerId: PeerId, wallpaper: TelegramWallpaper?) { + self.addOperation(.UpdateWallpaper(peerId: peerId, wallpaper: wallpaper)) + } + mutating func mergeUsers(_ users: [Api.User]) { self.addOperation(.MergeApiUsers(users)) @@ -660,7 +665,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, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .UpdateWallpaper, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization: break case let .AddMessages(messages, location): for message in messages { diff --git a/submodules/TelegramCore/Sources/ApiUtils/AdMessageAttribute.swift b/submodules/TelegramCore/Sources/ApiUtils/AdMessageAttribute.swift index 09498984de9..290a0d02381 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/AdMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/AdMessageAttribute.swift @@ -11,20 +11,23 @@ public final class AdMessageAttribute: MessageAttribute { case peer(id: EnginePeer.Id, message: EngineMessage.Id?, startParam: String?) case join(title: String, joinHash: String) case webPage(title: String, url: String) + case botApp(peerId: EnginePeer.Id, app: BotApp, startParam: String?) } public let opaqueId: Data public let messageType: MessageType public let displayAvatar: Bool public let target: MessageTarget + public let buttonText: String? public let sponsorInfo: String? public let additionalInfo: String? - public init(opaqueId: Data, messageType: MessageType, displayAvatar: Bool, target: MessageTarget, sponsorInfo: String?, additionalInfo: String?) { + public init(opaqueId: Data, messageType: MessageType, displayAvatar: Bool, target: MessageTarget, buttonText: String?, sponsorInfo: String?, additionalInfo: String?) { self.opaqueId = opaqueId self.messageType = messageType self.displayAvatar = displayAvatar self.target = target + self.buttonText = buttonText self.sponsorInfo = sponsorInfo self.additionalInfo = additionalInfo } diff --git a/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift b/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift index bc0ab4d0489..09181a7096e 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift @@ -61,7 +61,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? { return TelegramGroup(id: PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)), title: "", photo: [], participantCount: 0, role: .member, membership: .Removed, flags: [], defaultBannedRights: nil, migrationReference: nil, creationDate: 0, version: 0) case let .chatForbidden(id, title): return TelegramGroup(id: PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)), title: title, photo: [], participantCount: 0, role: .member, membership: .Removed, flags: [], defaultBannedRights: nil, migrationReference: nil, creationDate: 0, version: 0) - case let .channel(flags, flags2, id, accessHash, title, username, photo, date, restrictionReason, adminRights, bannedRights, defaultBannedRights, _, usernames, _, nameColor, backgroundEmojiId): + case let .channel(flags, flags2, id, accessHash, title, username, photo, date, restrictionReason, adminRights, bannedRights, defaultBannedRights, _, usernames, _, color): let isMin = (flags & (1 << 12)) != 0 let participationStatus: TelegramChannelParticipationStatus @@ -153,7 +153,17 @@ 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: nameColor.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId) + var nameColor: Int32? + var backgroundEmojiId: Int64? + if let color = color { + switch color { + case let .peerColor(_, color, backgroundEmojiIdValue): + nameColor = color + backgroundEmojiId = backgroundEmojiIdValue + } + } + + 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: nameColor.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: nil, profileBackgroundEmojiId: nil) case let .channelForbidden(flags, id, accessHash, title, untilDate): let info: TelegramChannelInfo if (flags & Int32(1 << 8)) != 0 { @@ -162,7 +172,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) + 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) } } @@ -170,7 +180,7 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? { switch rhs { case .chat, .chatEmpty, .chatForbidden, .channelForbidden: return parseTelegramGroupOrChannel(chat: rhs) - case let .channel(flags, flags2, _, accessHash, title, username, photo, _, _, _, _, defaultBannedRights, _, usernames, _, nameColor, backgroundEmojiId): + case let .channel(flags, flags2, _, accessHash, title, username, photo, _, _, _, _, defaultBannedRights, _, usernames, _, color): let isMin = (flags & (1 << 12)) != 0 if accessHash != nil && !isMin { return parseTelegramGroupOrChannel(chat: rhs) @@ -212,7 +222,17 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? { } } - 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: nameColor.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId) + var nameColorIndex: Int32? + var backgroundEmojiId: Int64? + if let color = color { + switch color { + case let .peerColor(_, color, backgroundEmojiIdValue): + nameColorIndex = color + backgroundEmojiId = backgroundEmojiIdValue + } + } + + 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: nil, profileBackgroundEmojiId: nil) } else { return parseTelegramGroupOrChannel(chat: rhs) } @@ -266,6 +286,6 @@ func mergeChannel(lhs: TelegramChannel?, rhs: TelegramChannel) -> TelegramChanne let storiesHidden: Bool? = rhs.storiesHidden ?? lhs.storiesHidden - 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) + 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) } diff --git a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift index 5c67dd630c0..4a060a74387 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift @@ -217,7 +217,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { } switch action { - case .messageActionChannelCreate, .messageActionChatDeletePhoto, .messageActionChatEditPhoto, .messageActionChatEditTitle, .messageActionEmpty, .messageActionPinMessage, .messageActionHistoryClear, .messageActionGameScore, .messageActionPaymentSent, .messageActionPaymentSentMe, .messageActionPhoneCall, .messageActionScreenshotTaken, .messageActionCustomAction, .messageActionBotAllowed, .messageActionSecureValuesSent, .messageActionSecureValuesSentMe, .messageActionContactSignUp, .messageActionGroupCall, .messageActionSetMessagesTTL, .messageActionGroupCallScheduled, .messageActionSetChatTheme, .messageActionChatJoinedByRequest, .messageActionWebViewDataSent, .messageActionWebViewDataSentMe, .messageActionGiftPremium, .messageActionTopicCreate, .messageActionTopicEdit, .messageActionSuggestProfilePhoto, .messageActionSetChatWallPaper, .messageActionSetSameChatWallPaper, .messageActionGiveawayLaunch: + case .messageActionChannelCreate, .messageActionChatDeletePhoto, .messageActionChatEditPhoto, .messageActionChatEditTitle, .messageActionEmpty, .messageActionPinMessage, .messageActionHistoryClear, .messageActionGameScore, .messageActionPaymentSent, .messageActionPaymentSentMe, .messageActionPhoneCall, .messageActionScreenshotTaken, .messageActionCustomAction, .messageActionBotAllowed, .messageActionSecureValuesSent, .messageActionSecureValuesSentMe, .messageActionContactSignUp, .messageActionGroupCall, .messageActionSetMessagesTTL, .messageActionGroupCallScheduled, .messageActionSetChatTheme, .messageActionChatJoinedByRequest, .messageActionWebViewDataSent, .messageActionWebViewDataSentMe, .messageActionGiftPremium, .messageActionTopicCreate, .messageActionTopicEdit, .messageActionSuggestProfilePhoto, .messageActionSetChatWallPaper, .messageActionGiveawayLaunch, .messageActionGiveawayResults: break case let .messageActionChannelMigrateFrom(_, chatId): result.append(PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(chatId))) @@ -261,12 +261,13 @@ func apiMessageAssociatedMessageIds(_ message: Api.Message) -> (replyIds: Refere let peerId: PeerId = chatPeerId.peerId switch replyTo { - case let .messageReplyHeader(_, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities): + case let .messageReplyHeader(_, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities, quoteOffset): let _ = replyHeader let _ = replyMedia let _ = replyToTopId let _ = quoteText let _ = quoteEntities + let _ = quoteOffset if let replyToMsgId = replyToMsgId { let targetId = MessageId(peerId: replyToPeerId?.peerId ?? peerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId) @@ -283,12 +284,13 @@ func apiMessageAssociatedMessageIds(_ message: Api.Message) -> (replyIds: Refere case let .messageService(_, id, _, chatPeerId, replyHeader, _, _, _): if let replyHeader = replyHeader { switch replyHeader { - case let .messageReplyHeader(_, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities): + case let .messageReplyHeader(_, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities, quoteOffset): let _ = replyHeader let _ = replyMedia let _ = replyToTopId let _ = quoteText let _ = quoteEntities + let _ = quoteOffset if let replyToMsgId = replyToMsgId { let targetId = MessageId(peerId: replyToPeerId?.peerId ?? chatPeerId.peerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId) @@ -586,14 +588,14 @@ extension StoreMessage { if let replyTo = replyTo { var threadMessageId: MessageId? switch replyTo { - case let .messageReplyHeader(innerFlags, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities): + case let .messageReplyHeader(innerFlags, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities, quoteOffset): let isForumTopic = (innerFlags & (1 << 3)) != 0 var quote: EngineMessageReplyQuote? let isQuote = (innerFlags & (1 << 9)) != 0 if quoteText != nil || replyMedia != nil { - quote = EngineMessageReplyQuote(text: quoteText ?? "", entities: messageTextEntitiesFromApiEntities(quoteEntities ?? []), media: textMediaAndExpirationTimerFromApiMedia(replyMedia, peerId).media) + quote = EngineMessageReplyQuote(text: quoteText ?? "", offset: quoteOffset.flatMap(Int.init), entities: messageTextEntitiesFromApiEntities(quoteEntities ?? []), media: textMediaAndExpirationTimerFromApiMedia(replyMedia, peerId).media) } if let replyToMsgId = replyToMsgId { @@ -873,11 +875,11 @@ extension StoreMessage { if let replyTo = replyTo { var threadMessageId: MessageId? switch replyTo { - case let .messageReplyHeader(innerFlags, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities): + case let .messageReplyHeader(innerFlags, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities, quoteOffset): var quote: EngineMessageReplyQuote? let isQuote = (innerFlags & (1 << 9)) != 0 if quoteText != nil || replyMedia != nil { - quote = EngineMessageReplyQuote(text: quoteText ?? "", entities: messageTextEntitiesFromApiEntities(quoteEntities ?? []), media: textMediaAndExpirationTimerFromApiMedia(replyMedia, peerId).media) + quote = EngineMessageReplyQuote(text: quoteText ?? "", offset: quoteOffset.flatMap(Int.init), entities: messageTextEntitiesFromApiEntities(quoteEntities ?? []), media: textMediaAndExpirationTimerFromApiMedia(replyMedia, peerId).media) } if let replyToMsgId = replyToMsgId { diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift index f4f4de886d1..20ea820be7a 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift @@ -123,14 +123,18 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe return TelegramMediaAction(action: .suggestedProfilePhoto(image: telegramMediaImageFromApiPhoto(photo))) case let .messageActionRequestedPeer(buttonId, peer): return TelegramMediaAction(action: .requestedPeer(buttonId: buttonId, peerId: peer.peerId)) - case let .messageActionSetChatWallPaper(wallpaper): - return TelegramMediaAction(action: .setChatWallpaper(wallpaper: TelegramWallpaper(apiWallpaper: wallpaper))) - case let .messageActionSetSameChatWallPaper(wallpaper): - return TelegramMediaAction(action: .setSameChatWallpaper(wallpaper: TelegramWallpaper(apiWallpaper: wallpaper))) + case let .messageActionSetChatWallPaper(flags, wallpaper): + if (flags & (1 << 0)) != 0 { + return TelegramMediaAction(action: .setSameChatWallpaper(wallpaper: TelegramWallpaper(apiWallpaper: wallpaper))) + } else { + return TelegramMediaAction(action: .setChatWallpaper(wallpaper: TelegramWallpaper(apiWallpaper: wallpaper), forBoth: (flags & (1 << 1)) != 0)) + } case let .messageActionGiftCode(flags, boostPeer, months, slug): return TelegramMediaAction(action: .giftCode(slug: slug, fromGiveaway: (flags & (1 << 0)) != 0, isUnclaimed: (flags & (1 << 2)) != 0, boostPeerId: boostPeer?.peerId, months: months)) case .messageActionGiveawayLaunch: return TelegramMediaAction(action: .giveawayLaunched) + case let .messageActionGiveawayResults(winners, unclaimed): + return TelegramMediaAction(action: .giveawayResults(winners: winners, unclaimed: unclaimed)) } } diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift index ebc1d017af5..b14c969788e 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift @@ -36,7 +36,7 @@ extension TelegramPeerUsername { extension TelegramUser { convenience init(user: Api.User) { switch user { - case let .user(flags, flags2, id, accessHash, firstName, lastName, username, phone, photo, _, _, restrictionReason, botInlinePlaceholder, _, emojiStatus, usernames, _, nameColor, backgroundEmojiId): + case let .user(flags, flags2, id, accessHash, firstName, lastName, username, phone, photo, _, _, restrictionReason, botInlinePlaceholder, _, emojiStatus, usernames, _, color, profileColor): let representations: [TelegramMediaImageRepresentation] = photo.flatMap(parsedTelegramProfilePhoto) ?? [] let isMin = (flags & (1 << 20)) != 0 @@ -96,15 +96,35 @@ extension TelegramUser { let restrictionInfo: PeerAccessRestrictionInfo? = restrictionReason.flatMap(PeerAccessRestrictionInfo.init(apiReasons:)) - self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, firstName: firstName, lastName: lastName, username: username, phone: phone, photo: representations, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColor.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId) + var nameColorIndex: Int32? + var backgroundEmojiId: Int64? + if let color = color { + switch color { + case let .peerColor(_, color, backgroundEmojiIdValue): + nameColorIndex = color + backgroundEmojiId = backgroundEmojiIdValue + } + } + + var profileColorIndex: Int32? + var profileBackgroundEmojiId: Int64? + if let profileColor = profileColor { + switch profileColor { + case let .peerColor(_, color, backgroundEmojiIdValue): + profileColorIndex = color + profileBackgroundEmojiId = backgroundEmojiIdValue + } + } + + self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, firstName: firstName, lastName: lastName, username: username, phone: phone, photo: representations, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId) case let .userEmpty(id): - self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), 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) + self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), 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) } } static func merge(_ lhs: TelegramUser?, rhs: Api.User) -> TelegramUser? { switch rhs { - case let .user(flags, _, _, rhsAccessHash, _, _, _, _, photo, _, _, restrictionReason, botInlinePlaceholder, _, emojiStatus, _, _, nameColor, backgroundEmojiId): + case let .user(flags, _, _, rhsAccessHash, _, _, _, _, photo, _, _, restrictionReason, botInlinePlaceholder, _, emojiStatus, _, _, nameColor, profileColor): let isMin = (flags & (1 << 20)) != 0 if !isMin { return TelegramUser(user: rhs) @@ -180,7 +200,27 @@ extension TelegramUser { accessHash = lhs.accessHash ?? rhsAccessHashValue } - return TelegramUser(id: lhs.id, accessHash: accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: lhs.username, phone: lhs.phone, photo: telegramPhoto, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), usernames: lhs.usernames, storiesHidden: lhs.storiesHidden, nameColor: nameColor.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId) + var nameColorIndex: Int32? + var backgroundEmojiId: Int64? + if let nameColor = nameColor { + switch nameColor { + case let .peerColor(_, color, backgroundEmojiIdValue): + nameColorIndex = color + backgroundEmojiId = backgroundEmojiIdValue + } + } + + var profileColorIndex: Int32? + var profileBackgroundEmojiId: Int64? + if let profileColor = profileColor { + switch profileColor { + case let .peerColor(_, color, backgroundEmojiIdValue): + profileColorIndex = color + profileBackgroundEmojiId = backgroundEmojiIdValue + } + } + + return TelegramUser(id: lhs.id, accessHash: accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: lhs.username, phone: lhs.phone, photo: telegramPhoto, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), usernames: lhs.usernames, storiesHidden: lhs.storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId) } else { return TelegramUser(user: rhs) } @@ -241,7 +281,7 @@ extension TelegramUser { storiesHidden = lhs.storiesHidden } - return TelegramUser(id: lhs.id, accessHash: accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: lhs.username, phone: lhs.phone, photo: photo, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus, usernames: lhs.usernames, storiesHidden: storiesHidden, nameColor: rhs.nameColor, backgroundEmojiId: rhs.backgroundEmojiId) + return TelegramUser(id: lhs.id, accessHash: accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: lhs.username, phone: lhs.phone, photo: photo, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus, usernames: lhs.usernames, storiesHidden: storiesHidden, nameColor: rhs.nameColor, backgroundEmojiId: rhs.backgroundEmojiId, profileColor: rhs.profileColor, profileBackgroundEmojiId: rhs.profileBackgroundEmojiId) } } } diff --git a/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift index d82251d9df8..1f5827d62b4 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift @@ -14,14 +14,17 @@ public struct EngineMessageReplyQuote: Codable, Equatable { case text = "t" case entities = "e" case media = "m" + case offset = "o" } public var text: String + public var offset: Int? public var entities: [MessageTextEntity] public var media: Media? - public init(text: String, entities: [MessageTextEntity], media: Media?) { + public init(text: String, offset: Int?, entities: [MessageTextEntity], media: Media?) { self.text = text + self.offset = offset self.entities = entities self.media = media } @@ -30,6 +33,7 @@ public struct EngineMessageReplyQuote: Codable, Equatable { let container = try decoder.container(keyedBy: CodingKeys.self) self.text = try container.decode(String.self, forKey: .text) + self.offset = (try container.decodeIfPresent(Int32.self, forKey: .offset)).flatMap(Int.init) self.entities = try container.decode([MessageTextEntity].self, forKey: .entities) if let mediaData = try container.decodeIfPresent(Data.self, forKey: .media) { @@ -43,6 +47,7 @@ public struct EngineMessageReplyQuote: Codable, Equatable { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.text, forKey: .text) + try container.encodeIfPresent(self.offset.flatMap(Int32.init(clamping:)), forKey: .offset) try container.encode(self.entities, forKey: .entities) if let media = self.media { let mediaEncoder = PostboxEncoder() @@ -55,6 +60,9 @@ public struct EngineMessageReplyQuote: Codable, Equatable { if lhs.text != rhs.text { return false } + if lhs.offset != rhs.offset { + return false + } if lhs.entities != rhs.entities { return false } @@ -583,7 +591,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, break } } - quote = EngineMessageReplyQuote(text: replyMessage.text, entities: messageTextEntitiesInRange(entities: replyMessage.textEntitiesAttribute?.entities ?? [], range: NSRange(location: 0, length: nsText.length), onlyQuoteable: true), media: replyMedia) + quote = EngineMessageReplyQuote(text: replyMessage.text, offset: nil, entities: messageTextEntitiesInRange(entities: replyMessage.textEntitiesAttribute?.entities ?? [], range: NSRange(location: 0, length: nsText.length), onlyQuoteable: true), media: replyMedia) } } attributes.append(ReplyMessageAttribute(messageId: replyToMessageId.messageId, threadMessageId: threadMessageId, quote: quote, isQuote: isQuote)) diff --git a/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift b/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift index 7025a9bed15..0c3352a2106 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift @@ -56,7 +56,7 @@ func messageContentToUpload(accountPeerId: PeerId, network: Network, postbox: Po return messageContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, forceReupload: forceReupload, isGrouped: isGrouped, passFetchProgress: passFetchProgress, forceNoBigParts: false, peerId: message.id.peerId, messageId: message.id, attributes: message.attributes, text: message.text, media: message.media) } -func messageContentToUpload(accountPeerId: PeerId, network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, isGrouped: Bool, passFetchProgress: Bool, forceNoBigParts: Bool, peerId: PeerId, messageId: MessageId?, attributes: [MessageAttribute], text: String, media: [Media]) -> MessageContentToUpload { +func messageContentToUpload(accountPeerId: PeerId, network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, isGrouped: Bool, passFetchProgress: Bool, forceNoBigParts: Bool, peerId: PeerId, messageId: MessageId?, attributes: [MessageAttribute], text: String, media: [Media], mediaReference: AnyMediaReference? = nil) -> MessageContentToUpload { var contextResult: OutgoingChatContextResultMessageAttribute? var autoremoveMessageAttribute: AutoremoveTimeoutMessageAttribute? var autoclearMessageAttribute: AutoclearTimeoutMessageAttribute? @@ -110,14 +110,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) { + } 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) { 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]) -> 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?) -> Signal? { if let image = media as? TelegramMediaImage, let largest = largestImageRepresentation(image.representations) { if peerId.namespace == Namespaces.Peer.SecretChat, let resource = largest.resource as? SecretFileMediaResource { return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .secretMedia(.inputEncryptedFile(id: resource.fileId, accessHash: resource.accessHash), resource.decryptedSize, resource.key), reuploadInfo: nil, cacheReferenceKey: nil))) @@ -140,13 +140,15 @@ func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Post return uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, forceReupload: true, isGrouped: isGrouped, passFetchProgress: false, forceNoBigParts: false, peerId: peerId, messageId: messageId, text: text, attributes: attributes, autoremoveMessageAttribute: autoremoveMessageAttribute, autoclearMessageAttribute: autoclearMessageAttribute, file: file) } else { if forceReupload { - let mediaReference: AnyMediaReference - if file.isSticker { - mediaReference = .standalone(media: file) + let finalMediaReference: AnyMediaReference + if let mediaReference = mediaReference { + finalMediaReference = mediaReference + } else if file.isSticker { + finalMediaReference = .standalone(media: file) } else { - mediaReference = .savedGif(media: file) + finalMediaReference = .savedGif(media: file) } - return revalidateMediaResourceReference(accountPeerId: accountPeerId, postbox: postbox, network: network, revalidationContext: revalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: mediaReference.resourceReference(file.resource), preferBackgroundReferenceRevalidation: false, continueInBackground: false), resource: resource) + return revalidateMediaResourceReference(accountPeerId: accountPeerId, postbox: postbox, network: network, revalidationContext: revalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: finalMediaReference.resourceReference(file.resource), preferBackgroundReferenceRevalidation: false, continueInBackground: false), resource: resource) |> mapError { _ -> PendingMessageUploadError in return .generic } diff --git a/submodules/TelegramCore/Sources/PendingMessages/PendingPeerMediaUploadManager.swift b/submodules/TelegramCore/Sources/PendingMessages/PendingPeerMediaUploadManager.swift index 626a0168475..c0f8c1c1090 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/PendingPeerMediaUploadManager.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/PendingPeerMediaUploadManager.swift @@ -15,7 +15,7 @@ public final class PeerMediaUploadingItem: Equatable { } public enum Content: Equatable { - case wallpaper(TelegramWallpaper) + case wallpaper(wallpaper: TelegramWallpaper, forBoth: Bool) } public let content: Content @@ -52,7 +52,7 @@ public final class PeerMediaUploadingItem: Equatable { private func uploadPeerMedia(postbox: Postbox, network: Network, stateManager: AccountStateManager, peerId: EnginePeer.Id, content: PeerMediaUploadingItem.Content) -> Signal { switch content { - case let .wallpaper(wallpaper): + case let .wallpaper(wallpaper, forBoth): if case let .image(representations, settings) = wallpaper, let resource = representations.last?.resource as? LocalFileMediaResource { return _internal_uploadWallpaper(postbox: postbox, network: network, resource: resource, settings: settings, forChat: true) |> mapError { error -> PeerMediaUploadingItem.Error in @@ -69,7 +69,7 @@ private func uploadPeerMedia(postbox: Postbox, network: Network, stateManager: A postbox.mediaBox.copyResourceData(from: resource.id, to: representation.resource.id, synchronous: true) } } - return _internal_setChatWallpaper(postbox: postbox, network: network, stateManager: stateManager, peerId: peerId, wallpaper: result, applyUpdates: false) + return _internal_setChatWallpaper(postbox: postbox, network: network, stateManager: stateManager, peerId: peerId, wallpaper: result, forBoth: forBoth, applyUpdates: false) |> mapError { error -> PeerMediaUploadingItem.Error in switch error { case .generic: @@ -85,7 +85,7 @@ private func uploadPeerMedia(postbox: Postbox, network: Network, stateManager: A } } else { - return _internal_setChatWallpaper(postbox: postbox, network: network, stateManager: stateManager, peerId: peerId, wallpaper: wallpaper, applyUpdates: false) + return _internal_setChatWallpaper(postbox: postbox, network: network, stateManager: stateManager, peerId: peerId, wallpaper: wallpaper, forBoth: forBoth, applyUpdates: false) |> mapError { error -> PeerMediaUploadingItem.Error in switch error { case .generic: @@ -124,8 +124,8 @@ private func generatePeerMediaMessage(network: Network, accountPeerId: EnginePee var media: [Media] = [] switch content { - case let .wallpaper(wallpaper): - media.append(TelegramMediaAction(action: .setChatWallpaper(wallpaper: wallpaper))) + case let .wallpaper(wallpaper, forBoth): + 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) diff --git a/submodules/TelegramCore/Sources/PendingMessages/RequestEditMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/RequestEditMessage.swift index f58525cf859..bb4bf0d33dc 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/RequestEditMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/RequestEditMessage.swift @@ -63,7 +63,7 @@ private func requestEditMessageInternal(accountPeerId: PeerId, postbox: Postbox, if let webpagePreviewAttribute = webpagePreviewAttribute { attributes.append(webpagePreviewAttribute) } - 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) + 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) } if let uploadSignal = generateUploadSignal(forceReupload) { uploadedMedia = .single(.progress(0.027)) diff --git a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift index 2d0e9aec51e..5e928d1c815 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift @@ -402,7 +402,7 @@ private func sendUploadedMessageContent( if threadId != nil { replyFlags |= 1 << 0 } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) } else if let replyToStoryId = replyToStoryId { if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) { flags |= 1 << 0 @@ -424,7 +424,7 @@ private func sendUploadedMessageContent( if threadId != nil { replyFlags |= 1 << 0 } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) } else if let replyToStoryId = replyToStoryId { if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) { flags |= 1 << 0 @@ -460,7 +460,7 @@ private func sendUploadedMessageContent( if threadId != nil { replyFlags |= 1 << 0 } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) } else if let replyToStoryId = replyToStoryId { if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) { flags |= 1 << 0 @@ -475,18 +475,18 @@ private func sendUploadedMessageContent( if let replyMessageId = replyMessageId { let replyFlags: Int32 = 0 - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) } else if let replyToStoryId = replyToStoryId { if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) { flags |= 1 << 0 replyTo = .inputReplyToStory(userId: inputUser, storyId: replyToStoryId.id) } else { let replyFlags: Int32 = 0 - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) } } else { let replyFlags: Int32 = 0 - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) } sendMessageRequest = network.request(Api.functions.messages.sendScreenshotNotification(peer: inputPeer, replyTo: replyTo, randomId: uniqueId)) @@ -625,7 +625,7 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M flags |= 1 << 0 let replyFlags: Int32 = 0 - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) } else if let replyToStoryId = replyToStoryId { if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) { flags |= 1 << 0 @@ -643,7 +643,7 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M flags |= 1 << 0 let replyFlags: Int32 = 0 - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) } else if let replyToStoryId = replyToStoryId { if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) { flags |= 1 << 0 diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index e5d67331be1..8ca7cd73bca 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1121,7 +1121,7 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: if updatedState.peers[peerId] == nil { updatedState.updatePeer(peerId, { peer in if peer == nil { - return TelegramUser(id: peerId, accessHash: nil, firstName: "Telegram Notifications", lastName: nil, username: nil, phone: nil, photo: [], botInfo: BotUserInfo(flags: [], inlinePlaceholder: nil), restrictionInfo: nil, flags: [.isVerified], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil) + return TelegramUser(id: peerId, accessHash: nil, firstName: "Telegram Notifications", lastName: nil, username: nil, phone: nil, photo: [], botInfo: BotUserInfo(flags: [], inlinePlaceholder: nil), restrictionInfo: nil, flags: [.isVerified], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) } else { return peer } @@ -1538,13 +1538,14 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: var replySubject: EngineMessageReplySubject? if let replyToMsgHeader = replyToMsgHeader { switch replyToMsgHeader { - case let .inputReplyToMessage(_, replyToMsgId, topMsgId, replyToPeerId, quoteText, quoteEntities): + case let .inputReplyToMessage(_, replyToMsgId, topMsgId, replyToPeerId, quoteText, quoteEntities, quoteOffset): let _ = topMsgId var quote: EngineMessageReplyQuote? if let quoteText = quoteText { quote = EngineMessageReplyQuote( text: quoteText, + offset: quoteOffset.flatMap(Int.init), entities: messageTextEntitiesFromApiEntities(quoteEntities ?? []), media: nil ) @@ -1738,6 +1739,8 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: case let .updateNewAuthorization(flags, hash, date, device, location): let isUnconfirmed = (flags & (1 << 0)) != 0 updatedState.updateNewAuthorization(isUnconfirmed: isUnconfirmed, hash: hash, date: date ?? 0, device: device ?? "", location: location ?? "") + case let .updatePeerWallpaper(_, peer, wallpaper): + updatedState.updateWallpaper(peerId: peer.peerId, wallpaper: wallpaper.flatMap { TelegramWallpaper(apiWallpaper: $0) }) default: break } @@ -3228,7 +3231,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation]) var currentAddScheduledMessages: 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, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization: + 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, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateWallpaper: if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty { result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location)) } @@ -3341,6 +3344,7 @@ func replayFinalState( var langPackDifferences: [String: [Api.LangPackDifference]] = [:] var pollLangPacks = Set() var updatedThemes: [Int64: TelegramTheme] = [:] + var updatedWallpapers: [PeerId: TelegramWallpaper?] = [:] var delayNotificatonsUntil: Int32? var peerActivityTimestamps: [PeerId: Int32] = [:] var syncChatListFilters = false @@ -3617,34 +3621,34 @@ func replayFinalState( }) } switch action.action { - case let .setChatTheme(emoticon): - transaction.updatePeerCachedData(peerIds: [message.id.peerId], update: { peerId, current in - var current = current - if current == nil { - if peerId.namespace == Namespaces.Peer.CloudUser { - current = CachedUserData() - } else if peerId.namespace == Namespaces.Peer.CloudGroup { - current = CachedGroupData() - } else if peerId.namespace == Namespaces.Peer.CloudChannel { - current = CachedChannelData() - } + case let .setChatTheme(emoticon): + transaction.updatePeerCachedData(peerIds: [message.id.peerId], update: { peerId, current in + var current = current + if current == nil { + if peerId.namespace == Namespaces.Peer.CloudUser { + current = CachedUserData() + } else if peerId.namespace == Namespaces.Peer.CloudGroup { + current = CachedGroupData() + } else if peerId.namespace == Namespaces.Peer.CloudChannel { + current = CachedChannelData() } - if let cachedData = current as? CachedUserData { - return cachedData.withUpdatedThemeEmoticon(!emoticon.isEmpty ? emoticon : nil) - } else if let cachedData = current as? CachedGroupData { - return cachedData.withUpdatedThemeEmoticon(!emoticon.isEmpty ? emoticon : nil) - } else if let cachedData = current as? CachedChannelData { - return cachedData.withUpdatedThemeEmoticon(!emoticon.isEmpty ? emoticon : nil) - } else { - return current - } - }) - case .groupCreated, .channelMigratedFromGroup: - let holesAtHistoryStart = transaction.getHole(containing: MessageId(peerId: chatPeerId, namespace: Namespaces.Message.Cloud, id: id.id - 1)) - for (space, _) in holesAtHistoryStart { - transaction.removeHole(peerId: chatPeerId, threadId: nil, namespace: Namespaces.Message.Cloud, space: space, range: 1 ... id.id) } - case let .setChatWallpaper(wallpaper), let .setSameChatWallpaper(wallpaper): + if let cachedData = current as? CachedUserData { + return cachedData.withUpdatedThemeEmoticon(!emoticon.isEmpty ? emoticon : nil) + } else if let cachedData = current as? CachedGroupData { + return cachedData.withUpdatedThemeEmoticon(!emoticon.isEmpty ? emoticon : nil) + } else if let cachedData = current as? CachedChannelData { + return cachedData.withUpdatedThemeEmoticon(!emoticon.isEmpty ? emoticon : nil) + } else { + return current + } + }) + case .groupCreated, .channelMigratedFromGroup: + let holesAtHistoryStart = transaction.getHole(containing: MessageId(peerId: chatPeerId, namespace: Namespaces.Message.Cloud, id: id.id - 1)) + for (space, _) in holesAtHistoryStart { + transaction.removeHole(peerId: chatPeerId, threadId: nil, namespace: Namespaces.Message.Cloud, space: space, range: 1 ... id.id) + } + case let .setChatWallpaper(wallpaper, _): if message.authorId == accountPeerId { transaction.updatePeerCachedData(peerIds: [message.id.peerId], update: { peerId, current in var current = current @@ -3660,8 +3664,8 @@ func replayFinalState( } }) } - default: - break + default: + break } } } @@ -4377,6 +4381,8 @@ func replayFinalState( updatedPeersNearby = peersNearby case let .UpdateTheme(theme): updatedThemes[theme.id] = theme + case let .UpdateWallpaper(peerId, wallpaper): + updatedWallpapers.updateValue(wallpaper, forKey: peerId) case .SyncChatListFilters: syncChatListFilters = true case let .UpdateChatListFilterOrder(order): @@ -4646,7 +4652,8 @@ func replayFinalState( isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: updatedReaction + myReaction: updatedReaction, + forwardInfo: item.forwardInfo )) if let entry = CodableEntry(updatedItem) { updatedPeerEntries[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: item.expirationTimestamp, isCloseFriends: item.isCloseFriends) @@ -4677,7 +4684,8 @@ func replayFinalState( isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: MessageReaction.Reaction(apiReaction: reaction) + myReaction: MessageReaction.Reaction(apiReaction: reaction), + forwardInfo: item.forwardInfo )) if let entry = CodableEntry(updatedItem) { transaction.setStory(id: StoryId(peerId: peerId, id: id), value: entry) @@ -5139,6 +5147,20 @@ func replayFinalState( }.start() } + if !updatedWallpapers.isEmpty { + for (peerId, wallpaper) in updatedWallpapers { + transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in + if let current = current as? CachedUserData { + return current.withUpdatedWallpaper(wallpaper) + } else { + var cachedData = CachedUserData() + cachedData = cachedData.withUpdatedWallpaper(wallpaper) + return cachedData + } + }) + } + } + addedIncomingMessageIds.append(contentsOf: addedSecretMessageIds) for (uniqueId, messageIdValue) in finalState.state.updatedOutgoingUniqueMessageIds { diff --git a/submodules/TelegramCore/Sources/State/AccountTaskManager.swift b/submodules/TelegramCore/Sources/State/AccountTaskManager.swift index 726ccbef8ed..f18a722046a 100644 --- a/submodules/TelegramCore/Sources/State/AccountTaskManager.swift +++ b/submodules/TelegramCore/Sources/State/AccountTaskManager.swift @@ -108,6 +108,7 @@ final class AccountTaskManager { tasks.add(managedRecentReactions(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) tasks.add(_internal_loadedStickerPack(postbox: self.stateManager.postbox, network: self.stateManager.network, reference: .iconStatusEmoji, forceActualized: true).start()) tasks.add(_internal_loadedStickerPack(postbox: self.stateManager.postbox, network: self.stateManager.network, reference: .iconTopicEmoji, forceActualized: true).start()) + tasks.add(managedPeerColorUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) self.managedTopReactionsDisposable.set(managedTopReactions(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) diff --git a/submodules/TelegramCore/Sources/State/FetchChatList.swift b/submodules/TelegramCore/Sources/State/FetchChatList.swift index 4b9c07b22d1..22fbe954a35 100644 --- a/submodules/TelegramCore/Sources/State/FetchChatList.swift +++ b/submodules/TelegramCore/Sources/State/FetchChatList.swift @@ -22,6 +22,7 @@ struct ParsedDialogs { let topMessageIds: [PeerId: MessageId] let storeMessages: [StoreMessage] let ttlPeriods: [PeerId: CachedPeerAutoremoveTimeout] + let viewForumAsMessages: [PeerId: Bool] let lowerNonPinnedIndex: MessageIndex? let referencedFolders: [PeerGroupId: PeerGroupUnreadCountersSummary] @@ -54,6 +55,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message], var channelStates: [PeerId: Int32] = [:] var topMessageIds: [PeerId: MessageId] = [:] var ttlPeriods: [PeerId: CachedPeerAutoremoveTimeout] = [:] + var viewForumAsMessages: [PeerId: Bool] = [:] var storeMessages: [StoreMessage] = [] var nonPinnedDialogsTopMessageIds = Set() @@ -98,6 +100,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message], apiNotificationSettings = peerNotificationSettings apiChannelPts = pts + viewForumAsMessages[peer.peerId] = (flags & (1 << 6)) != 0 ttlPeriods[peer.peerId] = .known(ttlPeriod.flatMap(CachedPeerAutoremoveTimeout.Value.init(peerValue:))) let isPinned = (flags & (1 << 2)) != 0 @@ -176,6 +179,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message], topMessageIds: topMessageIds, storeMessages: storeMessages, ttlPeriods: ttlPeriods, + viewForumAsMessages: viewForumAsMessages, lowerNonPinnedIndex: lowerNonPinnedIndex, referencedFolders: referencedFolders @@ -187,6 +191,7 @@ struct FetchedChatList { var peers: AccumulatedPeers var notificationSettings: [PeerId: PeerNotificationSettings] var ttlPeriods: [PeerId: CachedPeerAutoremoveTimeout] + var viewForumAsMessages: [PeerId: Bool] var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] var mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] var reactionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] @@ -294,6 +299,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo var peers = AccumulatedPeers() var notificationSettings: [PeerId: PeerNotificationSettings] = [:] var ttlPeriods: [PeerId: CachedPeerAutoremoveTimeout] = [:] + var viewForumAsMessages: [PeerId: Bool] = [:] var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:] var mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] = [:] var reactionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] = [:] @@ -304,6 +310,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo peers = peers.union(with: parsedRemoteChats.peers) notificationSettings.merge(parsedRemoteChats.notificationSettings, uniquingKeysWith: { _, updated in updated }) ttlPeriods.merge(parsedRemoteChats.ttlPeriods, uniquingKeysWith: { _, updated in updated }) + viewForumAsMessages.merge(parsedRemoteChats.viewForumAsMessages, uniquingKeysWith: { _, updated in updated }) readStates.merge(parsedRemoteChats.readStates, uniquingKeysWith: { _, updated in updated }) mentionTagSummaries.merge(parsedRemoteChats.mentionTagSummaries, uniquingKeysWith: { _, updated in updated }) reactionTagSummaries.merge(parsedRemoteChats.reactionTagSummaries, uniquingKeysWith: { _, updated in updated }) @@ -315,6 +322,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo peers = peers.union(with: parsedPinnedChats.peers) notificationSettings.merge(parsedPinnedChats.notificationSettings, uniquingKeysWith: { _, updated in updated }) ttlPeriods.merge(parsedPinnedChats.ttlPeriods, uniquingKeysWith: { _, updated in updated }) + viewForumAsMessages.merge(parsedPinnedChats.viewForumAsMessages, uniquingKeysWith: { _, updated in updated }) readStates.merge(parsedPinnedChats.readStates, uniquingKeysWith: { _, updated in updated }) mentionTagSummaries.merge(parsedPinnedChats.mentionTagSummaries, uniquingKeysWith: { _, updated in updated }) reactionTagSummaries.merge(parsedPinnedChats.reactionTagSummaries, uniquingKeysWith: { _, updated in updated }) @@ -338,6 +346,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo peers = peers.union(with: folderChats.peers) notificationSettings.merge(folderChats.notificationSettings, uniquingKeysWith: { _, updated in updated }) ttlPeriods.merge(folderChats.ttlPeriods, uniquingKeysWith: { _, updated in updated }) + viewForumAsMessages.merge(folderChats.viewForumAsMessages, uniquingKeysWith: { _, updated in updated }) readStates.merge(folderChats.readStates, uniquingKeysWith: { _, updated in updated }) mentionTagSummaries.merge(folderChats.mentionTagSummaries, uniquingKeysWith: { _, updated in updated }) reactionTagSummaries.merge(folderChats.reactionTagSummaries, uniquingKeysWith: { _, updated in updated }) @@ -372,6 +381,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo peers: peers, notificationSettings: notificationSettings, ttlPeriods: ttlPeriods, + viewForumAsMessages: viewForumAsMessages, readStates: readStates, mentionTagSummaries: mentionTagSummaries, reactionTagSummaries: reactionTagSummaries, diff --git a/submodules/TelegramCore/Sources/State/Holes.swift b/submodules/TelegramCore/Sources/State/Holes.swift index 723224a78fb..cbd0814b5a2 100644 --- a/submodules/TelegramCore/Sources/State/Holes.swift +++ b/submodules/TelegramCore/Sources/State/Holes.swift @@ -910,6 +910,18 @@ func fetchChatListHole(postbox: Postbox, network: Network, accountPeerId: PeerId } }) } + for (peerId, value) in fetchedChats.viewForumAsMessages { + if value { + transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in + if peerId.namespace == Namespaces.Peer.CloudChannel { + let current = (current as? CachedChannelData) ?? CachedChannelData() + return current.withUpdatedViewForumAsMessages(.known(value)) + } else { + return current + } + }) + } + } transaction.replaceChatListHole(groupId: groupId, index: hole.index, hole: fetchedChats.lowerNonPinnedIndex.flatMap(ChatListHole.init)) diff --git a/submodules/TelegramCore/Sources/State/ManagedAnimatedEmojiUpdates.swift b/submodules/TelegramCore/Sources/State/ManagedAnimatedEmojiUpdates.swift index e2950d57b53..8ab5a9e1f84 100644 --- a/submodules/TelegramCore/Sources/State/ManagedAnimatedEmojiUpdates.swift +++ b/submodules/TelegramCore/Sources/State/ManagedAnimatedEmojiUpdates.swift @@ -27,3 +27,5 @@ func managedGenericEmojiEffects(postbox: Postbox, network: Network) -> Signal then(.complete() |> suspendAwareDelay(2.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart } + + diff --git a/submodules/TelegramCore/Sources/State/ManagedAppConfigurationUpdates.swift b/submodules/TelegramCore/Sources/State/ManagedAppConfigurationUpdates.swift index 8b2ee5aa235..e62bc842638 100644 --- a/submodules/TelegramCore/Sources/State/ManagedAppConfigurationUpdates.swift +++ b/submodules/TelegramCore/Sources/State/ManagedAppConfigurationUpdates.swift @@ -33,6 +33,12 @@ func updateAppConfigurationOnce(postbox: Postbox, network: Network) -> Signal Bool { + if lhs === rhs { + return true + } + if lhs.colors != rhs.colors { + return false + } + return true + } + } + + public final class ColorOption: Codable, Equatable { + private enum CodingKeys: String, CodingKey { + case palette = "p" + case background = "b" + case stories = "s" + } + + public let palette: MultiColorPack + public let background: MultiColorPack + public let stories: MultiColorPack? + + public init(palette: MultiColorPack, background: MultiColorPack, stories: MultiColorPack?) { + self.palette = palette + self.background = background + self.stories = stories + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.palette = try container.decode(MultiColorPack.self, forKey: .palette) + self.background = try container.decode(MultiColorPack.self, forKey: .background) + self.stories = try container.decodeIfPresent(MultiColorPack.self, forKey: .stories) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.palette, forKey: .palette) + try container.encode(self.background, forKey: .background) + try container.encodeIfPresent(self.stories, forKey: .stories) + } + + public static func ==(lhs: ColorOption, rhs: ColorOption) -> Bool { + if lhs === rhs { + return true + } + if lhs.palette != rhs.palette { + return false + } + if lhs.background != rhs.background { + return false + } + if lhs.stories != rhs.stories { + return false + } + return true + } + } + + public final class ColorOptionPack: Codable, Equatable { + private enum CodingKeys: String, CodingKey { + case light = "l" + case dark = "d" + case isHidden = "h" + } + + public let light: ColorOption + public let dark: ColorOption? + public let isHidden: Bool + + public init(light: ColorOption, dark: ColorOption?, isHidden: Bool) { + self.light = light + self.dark = dark + self.isHidden = isHidden + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.light = try container.decode(ColorOption.self, forKey: .light) + self.dark = try container.decodeIfPresent(ColorOption.self, forKey: .dark) + self.isHidden = try container.decode(Bool.self, forKey: .isHidden) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.light, forKey: .light) + try container.encodeIfPresent(self.dark, forKey: .dark) + try container.encodeIfPresent(self.isHidden, forKey: .isHidden) + } + + public static func ==(lhs: ColorOptionPack, rhs: ColorOptionPack) -> Bool { + if lhs === rhs { + return true + } + if lhs.light != rhs.light { + return false + } + if lhs.dark != rhs.dark { + return false + } + if lhs.isHidden != rhs.isHidden { + return false + } + return true + } + } + + private enum CodingKeys: String, CodingKey { + case hash = "h" + case options = "o" + } + + public final class Option: Codable, Equatable { + private enum CodingKeys: String, CodingKey { + case key = "k" + case value = "v" + } + + public let key: Int32 + public let value: ColorOptionPack + + public init(key: Int32, value: ColorOptionPack) { + self.key = key + self.value = value + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.key = try container.decode(Int32.self, forKey: .key) + self.value = try container.decode(ColorOptionPack.self, forKey: .value) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.key, forKey: .key) + try container.encode(self.value, forKey: .value) + } + + public static func ==(lhs: Option, rhs: Option) -> Bool { + if lhs === rhs { + return true + } + if lhs.key != rhs.key { + return false + } + if lhs.value != rhs.value { + return false + } + return true + } + } + + public let hash: Int32 + public let options: [Option] + + public init(hash: Int32, options: [Option]) { + self.hash = hash + self.options = options + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.hash = try container.decode(Int32.self, forKey: .hash) + self.options = try container.decode([Option].self, forKey: .options) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.hash, forKey: .hash) + try container.encode(self.options, forKey: .options) + } + + public static func ==(lhs: EngineAvailableColorOptions, rhs: EngineAvailableColorOptions) -> Bool { + if lhs === rhs { + return true + } + if lhs.hash != rhs.hash { + return false + } + if lhs.options != rhs.options { + return false + } + return true + } +} + +private extension EngineAvailableColorOptions.ColorOption { + convenience init?(apiColors: Api.help.PeerColorSet) { + let palette: EngineAvailableColorOptions.MultiColorPack + let background: EngineAvailableColorOptions.MultiColorPack + let stories: EngineAvailableColorOptions.MultiColorPack? + + switch apiColors { + case let .peerColorSet(colors): + if colors.isEmpty { + return nil + } + palette = EngineAvailableColorOptions.MultiColorPack(colors: colors.map(UInt32.init(bitPattern:))) + background = palette + stories = nil + case let .peerColorProfileSet(palleteColors, bgColors, storyColors): + if palleteColors.isEmpty { + return nil + } + palette = EngineAvailableColorOptions.MultiColorPack(colors: palleteColors.map(UInt32.init(bitPattern:))) + + if bgColors.isEmpty { + return nil + } + background = EngineAvailableColorOptions.MultiColorPack(colors: bgColors.map(UInt32.init(bitPattern:))) + + if !storyColors.isEmpty { + stories = EngineAvailableColorOptions.MultiColorPack(colors: storyColors.map(UInt32.init(bitPattern:))) + } else { + stories = nil + } + } + + self.init(palette: palette, background: background, stories: stories) + } +} + +private extension EngineAvailableColorOptions { + convenience init(hash: Int32, apiColors: [Api.help.PeerColorOption]) { + var mappedOptions: [Option] = [] + for apiColor in apiColors { + switch apiColor { + case let .peerColorOption(flags, colorId, colors, darkColors): + let isHidden = (flags & (1 << 0)) != 0 + + let mappedColors = colors.flatMap(EngineAvailableColorOptions.ColorOption.init(apiColors:)) + let mappedDarkColors = darkColors.flatMap(EngineAvailableColorOptions.ColorOption.init(apiColors:)) + + if let mappedColors = mappedColors { + mappedOptions.append(Option(key: colorId, value: ColorOptionPack(light: mappedColors, dark: mappedDarkColors, isHidden: isHidden))) + } else if colorId >= 0 && colorId <= 6 { + let staticMap: [UInt32] = [ + 0xcc5049, + 0xd67722, + 0x955cdb, + 0x40a920, + 0x309eba, + 0x368ad1, + 0xc7508b + ] + let colorPack = MultiColorPack(colors: [staticMap[Int(colorId)]]) + let defaultColors = EngineAvailableColorOptions.ColorOption(palette: colorPack, background: colorPack, stories: nil) + mappedOptions.append(Option(key: colorId, value: ColorOptionPack(light: defaultColors, dark: nil, isHidden: isHidden))) + } + } + } + + self.init(hash: hash, options: mappedOptions) + } +} + +func managedPeerColorUpdates(postbox: Postbox, network: Network) -> Signal { + let poll = combineLatest( + _internal_fetchPeerColors(postbox: postbox, network: network, scope: .replies), + _internal_fetchPeerColors(postbox: postbox, network: network, scope: .profile) + ) + |> mapToSignal { _ -> Signal in + } + return (poll |> then(.complete() |> suspendAwareDelay(2.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart +} + +public enum PeerColorsScope { + case replies + case profile +} + +private func _internal_fetchPeerColors(postbox: Postbox, network: Network, scope: PeerColorsScope) -> Signal { + return postbox.transaction { transaction -> Int32 in + #if DEBUG + if "".isEmpty { + return 0 + } + #endif + return _internal_cachedAvailableColorOptions(transaction: transaction, scope: scope).hash + } + |> mapToSignal { hash -> Signal in + let signal: Signal + switch scope { + case .replies: + signal = network.request(Api.functions.help.getPeerColors(hash: hash)) + case .profile: + signal = network.request(Api.functions.help.getPeerProfileColors(hash: hash)) + } + + return signal + |> `catch` { _ -> Signal in + return .single(.peerColorsNotModified) + } + |> mapToSignal { result -> Signal in + switch result { + case .peerColorsNotModified: + return .complete() + case let .peerColors(hash, colors): + return postbox.transaction { transaction -> Void in + let value = EngineAvailableColorOptions(hash: hash, apiColors: colors) + _internal_setCachedAvailableColorOptions(transaction: transaction, scope: scope, value: value) + } + |> ignoreValues + } + } + } +} + +func _internal_cachedAvailableColorOptions(postbox: Postbox, scope: PeerColorsScope) -> Signal { + return postbox.transaction { transaction -> EngineAvailableColorOptions in + return _internal_cachedAvailableColorOptions(transaction: transaction, scope: scope) + } +} + +func _internal_observeAvailableColorOptions(postbox: Postbox, scope: PeerColorsScope) -> Signal { + let key = ValueBoxKey(length: 8) + switch scope { + case .replies: + key.setInt64(0, value: 0) + case .profile: + key.setInt64(0, value: 1) + } + let viewKey: PostboxViewKey = .cachedItem(ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.peerColorOptions, key: key)) + return postbox.combinedView(keys: [viewKey]) + |> map { views -> EngineAvailableColorOptions in + guard let view = views.views[viewKey] as? CachedItemView, let value = view.value?.get(EngineAvailableColorOptions.self) else { + return EngineAvailableColorOptions(hash: 0, options: []) + } + return value + } +} + +func _internal_cachedAvailableColorOptions(transaction: Transaction, scope: PeerColorsScope) -> EngineAvailableColorOptions { + let key = ValueBoxKey(length: 8) + switch scope { + case .replies: + key.setInt64(0, value: 0) + case .profile: + key.setInt64(0, value: 1) + } + + let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.peerColorOptions, key: key))?.get(EngineAvailableColorOptions.self) + if let cached = cached { + return cached + } else { + return EngineAvailableColorOptions(hash: 0, options: []) + } +} + +func _internal_setCachedAvailableColorOptions(transaction: Transaction, scope: PeerColorsScope, value: EngineAvailableColorOptions) { + let key = ValueBoxKey(length: 8) + switch scope { + case .replies: + key.setInt64(0, value: 0) + case .profile: + key.setInt64(0, value: 1) + } + + if let entry = CodableEntry(value) { + transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.peerColorOptions, key: key), entry: entry) + } +} diff --git a/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift index f4aed3660be..3f85200f212 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift @@ -167,8 +167,10 @@ private func synchronizeChatInputState(transaction: Transaction, postbox: Postbo var quoteText: String? var quoteEntities: [Api.MessageEntity]? + var quoteOffset: Int32? if let replyQuote = replySubject.quote { quoteText = replyQuote.text + quoteOffset = replyQuote.offset.flatMap { Int32(clamping: $0) } if !replyQuote.entities.isEmpty { var associatedPeers = SimpleDictionary() @@ -194,16 +196,19 @@ private func synchronizeChatInputState(transaction: Transaction, postbox: Postbo if quoteEntities != nil { innerFlags |= 1 << 3 } + if quoteOffset != nil { + innerFlags |= 1 << 4 + } if !discard { - replyTo = .inputReplyToMessage(flags: innerFlags, replyToMsgId: replySubject.messageId.id, topMsgId: topMsgId, replyToPeerId: replyToPeer, quoteText: quoteText, quoteEntities: quoteEntities) + replyTo = .inputReplyToMessage(flags: innerFlags, replyToMsgId: replySubject.messageId.id, topMsgId: topMsgId, replyToPeerId: replyToPeer, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset) } } else if let topMsgId = topMsgId { flags |= 1 << 0 var innerFlags: Int32 = 0 innerFlags |= 1 << 0 - replyTo = .inputReplyToMessage(flags: innerFlags, replyToMsgId: topMsgId, topMsgId: topMsgId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil) + replyTo = .inputReplyToMessage(flags: innerFlags, replyToMsgId: topMsgId, topMsgId: topMsgId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) } return network.request(Api.functions.messages.saveDraft(flags: flags, replyTo: replyTo, peer: inputPeer, message: inputState?.text ?? "", entities: apiEntitiesFromMessageTextEntities(inputState?.entities ?? [], associatedPeers: SimpleDictionary()), media: nil)) diff --git a/submodules/TelegramCore/Sources/State/MessageReactions.swift b/submodules/TelegramCore/Sources/State/MessageReactions.swift index 6d1e4a7a40c..9f87c2e72ad 100644 --- a/submodules/TelegramCore/Sources/State/MessageReactions.swift +++ b/submodules/TelegramCore/Sources/State/MessageReactions.swift @@ -597,6 +597,7 @@ public final class EngineMessageReactionListContext { public enum UpdatePeerAllowedReactionsError { case generic + case boostRequired } func _internal_updatePeerAllowedReactions(account: Account, peerId: PeerId, allowedReactions: PeerAllowedReactions) -> Signal { @@ -620,11 +621,20 @@ func _internal_updatePeerAllowedReactions(account: Account, peerId: PeerId, allo } return account.network.request(Api.functions.messages.setChatAvailableReactions(peer: inputPeer, availableReactions: mappedReactions)) - |> mapError { _ -> UpdatePeerAllowedReactionsError in - return .generic + |> map(Optional.init) + |> `catch` { error -> Signal in + if error.errorDescription == "CHAT_NOT_MODIFIED" { + return .single(nil) + } else if error.errorDescription == "BOOSTS_REQUIRED" { + return .fail(.boostRequired) + } else { + return .fail(.generic) + } } |> mapToSignal { result -> Signal in - account.stateManager.addUpdates(result) + if let result = result { + account.stateManager.addUpdates(result) + } return account.postbox.transaction { transaction -> Void in transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in diff --git a/submodules/TelegramCore/Sources/State/PendingMessageManager.swift b/submodules/TelegramCore/Sources/State/PendingMessageManager.swift index 3c3bd5ae1a4..371e864c9d2 100644 --- a/submodules/TelegramCore/Sources/State/PendingMessageManager.swift +++ b/submodules/TelegramCore/Sources/State/PendingMessageManager.swift @@ -950,9 +950,11 @@ public final class PendingMessageManager { var quoteText: String? var quoteEntities: [Api.MessageEntity]? + var quoteOffset: Int32? if let replyQuote = replyQuote { replyFlags |= 1 << 2 quoteText = replyQuote.text + quoteOffset = replyQuote.offset.flatMap { Int32.init(clamping: $0) } if !replyQuote.entities.isEmpty { replyFlags |= 1 << 3 @@ -968,9 +970,13 @@ public final class PendingMessageManager { } quoteEntities = apiEntitiesFromMessageTextEntities(replyQuote.entities, associatedPeers: associatedPeers) } + + if quoteOffset != nil { + replyFlags |= 1 << 4 + } } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset) } else if let replyToStoryId = replyToStoryId { if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) { flags |= 1 << 0 @@ -1236,9 +1242,11 @@ public final class PendingMessageManager { var quoteText: String? var quoteEntities: [Api.MessageEntity]? + var quoteOffset: Int32? if let replyQuote = replyQuote { replyFlags |= 1 << 2 quoteText = replyQuote.text + quoteOffset = replyQuote.offset.flatMap { Int32.init(clamping: $0) } if !replyQuote.entities.isEmpty { replyFlags |= 1 << 3 @@ -1254,9 +1262,13 @@ public final class PendingMessageManager { } quoteEntities = apiEntitiesFromMessageTextEntities(replyQuote.entities, associatedPeers: associatedPeers) } + + if quoteOffset != nil { + replyFlags |= 1 << 4 + } } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: message.threadId.flatMap(Int32.init(clamping:)), replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: message.threadId.flatMap(Int32.init(clamping:)), replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset) } else if let replyToStoryId = replyToStoryId { if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) { flags |= 1 << 0 @@ -1295,10 +1307,16 @@ public final class PendingMessageManager { var quoteText: String? var quoteEntities: [Api.MessageEntity]? + var quoteOffset: Int32? if let replyQuote = replyQuote { replyFlags |= 1 << 2 quoteText = replyQuote.text + quoteOffset = replyQuote.offset.flatMap { Int32.init(clamping: $0) } + if quoteOffset != nil { + replyFlags |= 1 << 4 + } + if !replyQuote.entities.isEmpty { replyFlags |= 1 << 3 var associatedPeers = SimpleDictionary() @@ -1315,7 +1333,7 @@ public final class PendingMessageManager { } } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: message.threadId.flatMap(Int32.init(clamping:)), replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: message.threadId.flatMap(Int32.init(clamping:)), replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset) } else if let replyToStoryId = replyToStoryId { if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) { flags |= 1 << 0 @@ -1368,10 +1386,16 @@ public final class PendingMessageManager { var quoteText: String? var quoteEntities: [Api.MessageEntity]? + var quoteOffset: Int32? if let replyQuote = replyQuote { replyFlags |= 1 << 2 quoteText = replyQuote.text + quoteOffset = replyQuote.offset.flatMap { Int32.init(clamping: $0) } + if quoteOffset != nil { + replyFlags |= 1 << 4 + } + if !replyQuote.entities.isEmpty { replyFlags |= 1 << 3 var associatedPeers = SimpleDictionary() @@ -1388,7 +1412,7 @@ public final class PendingMessageManager { } } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: message.threadId.flatMap(Int32.init(clamping:)), replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: message.threadId.flatMap(Int32.init(clamping:)), replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset) } else if let replyToStoryId = replyToStoryId { if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) { flags |= 1 << 0 @@ -1403,18 +1427,18 @@ public final class PendingMessageManager { if let replyMessageId = replyMessageId { let replyFlags: Int32 = 0 - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) } else if let replyToStoryId = replyToStoryId { if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) { flags |= 1 << 0 replyTo = .inputReplyToStory(userId: inputUser, storyId: replyToStoryId.id) } else { let replyFlags: Int32 = 0 - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) } } else { let replyFlags: Int32 = 0 - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) } sendMessageRequest = network.request(Api.functions.messages.sendScreenshotNotification(peer: inputPeer, replyTo: replyTo, randomId: uniqueId)) diff --git a/submodules/TelegramCore/Sources/State/ResetState.swift b/submodules/TelegramCore/Sources/State/ResetState.swift index 2f27ea17e18..2e7d2cbab3a 100644 --- a/submodules/TelegramCore/Sources/State/ResetState.swift +++ b/submodules/TelegramCore/Sources/State/ResetState.swift @@ -71,6 +71,18 @@ func _internal_resetAccountState(postbox: Postbox, network: Network, accountPeer } }) } + for (peerId, value) in fetchedChats.viewForumAsMessages { + if value { + transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in + if peerId.namespace == Namespaces.Peer.CloudChannel { + let current = (current as? CachedChannelData) ?? CachedChannelData() + return current.withUpdatedViewForumAsMessages(.known(value)) + } else { + return current + } + }) + } + } for hole in transaction.allChatListHoles(groupId: .root) { transaction.replaceChatListHole(groupId: .root, index: hole.index, hole: nil) diff --git a/submodules/TelegramCore/Sources/State/Serialization.swift b/submodules/TelegramCore/Sources/State/Serialization.swift index ec47cea9fef..89018bee15c 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 166 + return 167 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift b/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift index fbd58bf03b5..863289600d6 100644 --- a/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift +++ b/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift @@ -182,7 +182,7 @@ extension Api.Chat { return PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)) case let .chatForbidden(id, _): return PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)) - case let .channel(_, _, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .channel(_, _, id, _, _, _, _, _, _, _, _, _, _, _, _, _): return PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)) case let .channelForbidden(_, id, _, _, _): return PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)) diff --git a/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift b/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift index 5922c615712..6735c5c1292 100644 --- a/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift +++ b/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift @@ -25,6 +25,7 @@ public struct UserLimitsConfiguration: Equatable { public let maxGiveawayChannelsCount: Int32 public let maxGiveawayCountriesCount: Int32 public let maxGiveawayPeriodSeconds: Int32 + public let maxChannelRecommendationsCount: Int32 public static var defaultValue: UserLimitsConfiguration { return UserLimitsConfiguration( @@ -50,7 +51,8 @@ public struct UserLimitsConfiguration: Equatable { maxStoriesSuggestedReactions: 1, maxGiveawayChannelsCount: 10, maxGiveawayCountriesCount: 10, - maxGiveawayPeriodSeconds: 86400 * 7 + maxGiveawayPeriodSeconds: 86400 * 7, + maxChannelRecommendationsCount: 10 ) } @@ -77,7 +79,8 @@ public struct UserLimitsConfiguration: Equatable { maxStoriesSuggestedReactions: Int32, maxGiveawayChannelsCount: Int32, maxGiveawayCountriesCount: Int32, - maxGiveawayPeriodSeconds: Int32 + maxGiveawayPeriodSeconds: Int32, + maxChannelRecommendationsCount: Int32 ) { self.maxPinnedChatCount = maxPinnedChatCount self.maxArchivedPinnedChatCount = maxArchivedPinnedChatCount @@ -102,6 +105,7 @@ public struct UserLimitsConfiguration: Equatable { self.maxGiveawayChannelsCount = maxGiveawayChannelsCount self.maxGiveawayCountriesCount = maxGiveawayCountriesCount self.maxGiveawayPeriodSeconds = maxGiveawayPeriodSeconds + self.maxChannelRecommendationsCount = maxChannelRecommendationsCount } } @@ -149,5 +153,6 @@ extension UserLimitsConfiguration { self.maxGiveawayChannelsCount = getGeneralValue("giveaway_add_peers_max", orElse: defaultValue.maxGiveawayChannelsCount) self.maxGiveawayCountriesCount = getGeneralValue("giveaway_countries_max", orElse: defaultValue.maxGiveawayCountriesCount) self.maxGiveawayPeriodSeconds = getGeneralValue("giveaway_period_max", orElse: defaultValue.maxGiveawayPeriodSeconds) + self.maxChannelRecommendationsCount = getValue("recommended_channels_limit", orElse: defaultValue.maxChannelRecommendationsCount) } } diff --git a/submodules/TelegramCore/Sources/MessageStatistics.swift b/submodules/TelegramCore/Sources/Statistics/MessageStatistics.swift similarity index 72% rename from submodules/TelegramCore/Sources/MessageStatistics.swift rename to submodules/TelegramCore/Sources/Statistics/MessageStatistics.swift index 54c9ab9f5ce..81e0e51d968 100644 --- a/submodules/TelegramCore/Sources/MessageStatistics.swift +++ b/submodules/TelegramCore/Sources/Statistics/MessageStatistics.swift @@ -7,14 +7,18 @@ import MtProtoKit public struct MessageStats: Equatable { public let views: Int public let forwards: Int + public let reactions: Int public let interactionsGraph: StatsGraph public let interactionsGraphDelta: Int64 + public let reactionsGraph: StatsGraph - init(views: Int, forwards: Int, interactionsGraph: StatsGraph, interactionsGraphDelta: Int64) { + init(views: Int, forwards: Int, reactions: Int, interactionsGraph: StatsGraph, interactionsGraphDelta: Int64, reactionsGraph: StatsGraph) { self.views = views self.forwards = forwards + self.reactions = reactions self.interactionsGraph = interactionsGraph self.interactionsGraphDelta = interactionsGraphDelta + self.reactionsGraph = reactionsGraph } public static func == (lhs: MessageStats, rhs: MessageStats) -> Bool { @@ -24,17 +28,23 @@ public struct MessageStats: Equatable { if lhs.forwards != rhs.forwards { return false } + if lhs.reactions != rhs.reactions { + return false + } if lhs.interactionsGraph != rhs.interactionsGraph { return false } if lhs.interactionsGraphDelta != rhs.interactionsGraphDelta { return false } + if lhs.reactionsGraph != rhs.reactionsGraph { + return false + } return true } public func withUpdatedInteractionsGraph(_ interactionsGraph: StatsGraph) -> MessageStats { - return MessageStats(views: self.views, forwards: self.forwards, interactionsGraph: interactionsGraph, interactionsGraphDelta: self.interactionsGraphDelta) + return MessageStats(views: self.views, forwards: self.forwards, reactions: self.reactions, interactionsGraph: interactionsGraph, interactionsGraphDelta: self.interactionsGraphDelta, reactionsGraph: self.reactionsGraph) } } @@ -42,15 +52,15 @@ public struct MessageStatsContextState: Equatable { public var stats: MessageStats? } -private func requestMessageStats(postbox: Postbox, network: Network, datacenterId: Int32, messageId: MessageId, dark: Bool = false) -> Signal { - return postbox.transaction { transaction -> (Peer, Message)? in - if let peer = transaction.getPeer(messageId.peerId), let message = transaction.getMessage(messageId) { - return (peer, message) +private func requestMessageStats(postbox: Postbox, network: Network, messageId: MessageId, dark: Bool = false) -> Signal { + return postbox.transaction { transaction -> (Int32, Peer, Message)? in + if let peer = transaction.getPeer(messageId.peerId), let message = transaction.getMessage(messageId), let cachedData = transaction.getPeerCachedData(peerId: messageId.peerId) as? CachedChannelData { + return (cachedData.statsDatacenterId, peer, message) } else { return nil } - } |> mapToSignal { peerAndMessage -> Signal in - guard let (peer, message) = peerAndMessage, let inputChannel = apiInputChannel(peer) else { + } |> mapToSignal { data -> Signal in + guard let (datacenterId, peer, message) = data, let inputChannel = apiInputChannel(peer) else { return .never() } @@ -73,18 +83,23 @@ private func requestMessageStats(postbox: Postbox, network: Network, datacenterI var views: Int = 0 var forwards: Int = 0 + var reactions: Int = 0 for attribute in message.attributes { if let viewsAttribute = attribute as? ViewCountMessageAttribute { views = viewsAttribute.count } else if let forwardsAttribute = attribute as? ForwardCountMessageAttribute { forwards = forwardsAttribute.count + } else if let reactionsAttribute = attribute as? ReactionsMessageAttribute { + reactions = Int(reactionsAttribute.reactions.reduce(0, { partialResult, reaction in + return partialResult + reaction.count + })) } } return signal |> mapToSignal { result -> Signal in - if case let .messageStats(apiViewsGraph) = result { - let interactionsGraph = StatsGraph(apiStatsGraph: apiViewsGraph) + if case let .messageStats(apiInteractionsGraph, apiReactionsGraph) = result { + let interactionsGraph = StatsGraph(apiStatsGraph: apiInteractionsGraph) var interactionsGraphDelta: Int64 = 86400 if case let .Loaded(_, data) = interactionsGraph { if let start = data.range(of: "[\"x\",") { @@ -101,8 +116,15 @@ private func requestMessageStats(postbox: Postbox, network: Network, datacenterI } } } - - return .single(MessageStats(views: views, forwards: forwards, interactionsGraph: interactionsGraph, interactionsGraphDelta: interactionsGraphDelta)) + let reactionsGraph = StatsGraph(apiStatsGraph: apiReactionsGraph) + return .single(MessageStats( + views: views, + forwards: forwards, + reactions: reactions, + interactionsGraph: interactionsGraph, + interactionsGraphDelta: interactionsGraphDelta, + reactionsGraph: reactionsGraph + )) } else { return .single(nil) } @@ -114,7 +136,6 @@ private func requestMessageStats(postbox: Postbox, network: Network, datacenterI private final class MessageStatsContextImpl { private let postbox: Postbox private let network: Network - private let datacenterId: Int32 private let messageId: MessageId private var _state: MessageStatsContextState { @@ -132,12 +153,11 @@ private final class MessageStatsContextImpl { private let disposable = MetaDisposable() private let disposables = DisposableDict() - init(postbox: Postbox, network: Network, datacenterId: Int32, messageId: MessageId) { + init(postbox: Postbox, network: Network, messageId: MessageId) { assert(Queue.mainQueue().isCurrent()) self.postbox = postbox self.network = network - self.datacenterId = datacenterId self.messageId = messageId self._state = MessageStatsContextState(stats: nil) self._statePromise.set(.single(self._state)) @@ -154,7 +174,7 @@ private final class MessageStatsContextImpl { private func load() { assert(Queue.mainQueue().isCurrent()) - self.disposable.set((requestMessageStats(postbox: self.postbox, network: self.network, datacenterId: self.datacenterId, messageId: self.messageId) + self.disposable.set((requestMessageStats(postbox: self.postbox, network: self.network, messageId: self.messageId) |> deliverOnMainQueue).start(next: { [weak self] stats in if let strongSelf = self { strongSelf._state = MessageStatsContextState(stats: stats) @@ -165,7 +185,7 @@ private final class MessageStatsContextImpl { func loadDetailedGraph(_ graph: StatsGraph, x: Int64) -> Signal { if let token = graph.token { - return requestGraph(network: self.network, datacenterId: self.datacenterId, token: token, x: x) + return requestGraph(postbox: self.postbox, network: self.network, peerId: self.messageId.peerId, token: token, x: x) } else { return .single(nil) } @@ -187,9 +207,9 @@ public final class MessageStatsContext { } } - public init(postbox: Postbox, network: Network, datacenterId: Int32, messageId: MessageId) { + public init(account: Account, messageId: MessageId) { self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: { - return MessageStatsContextImpl(postbox: postbox, network: network, datacenterId: datacenterId, messageId: messageId) + return MessageStatsContextImpl(postbox: account.postbox, network: account.network, messageId: messageId) }) } @@ -206,4 +226,3 @@ public final class MessageStatsContext { } } } - diff --git a/submodules/TelegramCore/Sources/PeerStatistics.swift b/submodules/TelegramCore/Sources/Statistics/PeerStatistics.swift similarity index 62% rename from submodules/TelegramCore/Sources/PeerStatistics.swift rename to submodules/TelegramCore/Sources/Statistics/PeerStatistics.swift index 87345389395..c0a080c0568 100644 --- a/submodules/TelegramCore/Sources/PeerStatistics.swift +++ b/submodules/TelegramCore/Sources/Statistics/PeerStatistics.swift @@ -48,17 +48,34 @@ public enum StatsGraph: Equatable { } } -public struct ChannelStatsMessageInteractions: Equatable { - public let messageId: MessageId +public struct ChannelStatsPostInteractions: Equatable { + public enum PostId: Hashable { + case message(id: EngineMessage.Id) + case story(peerId: EnginePeer.Id, id: Int32) + } + + public let postId: PostId public let views: Int32 public let forwards: Int32 + public let reactions: Int32 + + public init(postId: PostId, views: Int32, forwards: Int32, reactions: Int32) { + self.postId = postId + self.views = views + self.forwards = forwards + self.reactions = reactions + } } -public final class ChannelStats: Equatable { +public struct ChannelStats: Equatable { public let period: StatsDateRange public let followers: StatsValue public let viewsPerPost: StatsValue public let sharesPerPost: StatsValue + public let reactionsPerPost: StatsValue + public let viewsPerStory: StatsValue + public let sharesPerStory: StatsValue + public let reactionsPerStory: StatsValue public let enabledNotifications: StatsPercentValue public let growthGraph: StatsGraph public let followersGraph: StatsGraph @@ -69,13 +86,43 @@ public final class ChannelStats: Equatable { public let viewsBySourceGraph: StatsGraph public let newFollowersBySourceGraph: StatsGraph public let languagesGraph: StatsGraph - public let messageInteractions: [ChannelStatsMessageInteractions] - - init(period: StatsDateRange, followers: StatsValue, viewsPerPost: StatsValue, sharesPerPost: StatsValue, enabledNotifications: StatsPercentValue, growthGraph: StatsGraph, followersGraph: StatsGraph, muteGraph: StatsGraph, topHoursGraph: StatsGraph, interactionsGraph: StatsGraph, instantPageInteractionsGraph: StatsGraph, viewsBySourceGraph: StatsGraph, newFollowersBySourceGraph: StatsGraph, languagesGraph: StatsGraph, messageInteractions: [ChannelStatsMessageInteractions]) { + public let reactionsByEmotionGraph: StatsGraph + public let storyInteractionsGraph: StatsGraph + public let storyReactionsByEmotionGraph: StatsGraph + public let postInteractions: [ChannelStatsPostInteractions] + + init( + period: StatsDateRange, + followers: StatsValue, + viewsPerPost: StatsValue, + sharesPerPost: StatsValue, + reactionsPerPost: StatsValue, + viewsPerStory: StatsValue, + sharesPerStory: StatsValue, + reactionsPerStory: StatsValue, + enabledNotifications: StatsPercentValue, + growthGraph: StatsGraph, + followersGraph: StatsGraph, + muteGraph: StatsGraph, + topHoursGraph: StatsGraph, + interactionsGraph: StatsGraph, + instantPageInteractionsGraph: StatsGraph, + viewsBySourceGraph: StatsGraph, + newFollowersBySourceGraph: StatsGraph, + languagesGraph: StatsGraph, + reactionsByEmotionGraph: StatsGraph, + storyInteractionsGraph: StatsGraph, + storyReactionsByEmotionGraph: StatsGraph, + postInteractions: [ChannelStatsPostInteractions] + ) { self.period = period self.followers = followers self.viewsPerPost = viewsPerPost self.sharesPerPost = sharesPerPost + self.reactionsPerPost = reactionsPerPost + self.viewsPerStory = viewsPerStory + self.sharesPerStory = sharesPerStory + self.reactionsPerStory = reactionsPerStory self.enabledNotifications = enabledNotifications self.growthGraph = growthGraph self.followersGraph = followersGraph @@ -86,7 +133,10 @@ public final class ChannelStats: Equatable { self.viewsBySourceGraph = viewsBySourceGraph self.newFollowersBySourceGraph = newFollowersBySourceGraph self.languagesGraph = languagesGraph - self.messageInteractions = messageInteractions + self.reactionsByEmotionGraph = reactionsByEmotionGraph + self.storyInteractionsGraph = storyInteractionsGraph + self.storyReactionsByEmotionGraph = storyReactionsByEmotionGraph + self.postInteractions = postInteractions } public static func == (lhs: ChannelStats, rhs: ChannelStats) -> Bool { @@ -132,46 +182,65 @@ public final class ChannelStats: Equatable { if lhs.languagesGraph != rhs.languagesGraph { return false } - if lhs.messageInteractions != rhs.messageInteractions { + if lhs.reactionsByEmotionGraph != rhs.reactionsByEmotionGraph { + return false + } + if lhs.storyInteractionsGraph != rhs.storyInteractionsGraph { + return false + } + if lhs.storyReactionsByEmotionGraph != rhs.storyReactionsByEmotionGraph { + return false + } + if lhs.postInteractions != rhs.postInteractions { return false } return true } public func withUpdatedGrowthGraph(_ growthGraph: StatsGraph) -> ChannelStats { - return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, messageInteractions: self.messageInteractions) + return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions) } public func withUpdatedFollowersGraph(_ followersGraph: StatsGraph) -> ChannelStats { - return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, messageInteractions: self.messageInteractions) + return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions) } public func withUpdatedMuteGraph(_ muteGraph: StatsGraph) -> ChannelStats { - return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, messageInteractions: self.messageInteractions) + return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions) } public func withUpdatedTopHoursGraph(_ viewsByHourGraph: StatsGraph) -> ChannelStats { - return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: viewsByHourGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, messageInteractions: self.messageInteractions) + return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: viewsByHourGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions) } public func withUpdatedInteractionsGraph(_ interactionsGraph: StatsGraph) -> ChannelStats { - return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, messageInteractions: self.messageInteractions) + return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions) } public func withUpdatedInstantPageInteractionsGraph(_ instantPageInteractionsGraph: StatsGraph) -> ChannelStats { - return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, messageInteractions: self.messageInteractions) + return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions) } public func withUpdatedViewsBySourceGraph(_ viewsBySourceGraph: StatsGraph) -> ChannelStats { - return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, messageInteractions: self.messageInteractions) + return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions) } public func withUpdatedNewFollowersBySourceGraph(_ newFollowersBySourceGraph: StatsGraph) -> ChannelStats { - return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: newFollowersBySourceGraph, languagesGraph: self.languagesGraph, messageInteractions: self.messageInteractions) + return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions) } public func withUpdatedLanguagesGraph(_ languagesGraph: StatsGraph) -> ChannelStats { - return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: languagesGraph, messageInteractions: self.messageInteractions) + return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions) + } + + public func withUpdatedReactionsByEmotionGraph(_ reactionsByEmotionGraph: StatsGraph) -> ChannelStats { + return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions) + } + public func withUpdatedStoryInteractionsGraph(_ storyInteractionsGraph: StatsGraph) -> ChannelStats { + return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions) + } + public func withUpdatedStoryReactionsByEmotionGraph(_ storyReactionsByEmotionGraph: StatsGraph) -> ChannelStats { + return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: storyReactionsByEmotionGraph, postInteractions: self.postInteractions) } } @@ -179,11 +248,14 @@ public struct ChannelStatsContextState: Equatable { public var stats: ChannelStats? } -private func requestChannelStats(postbox: Postbox, network: Network, datacenterId: Int32, peerId: PeerId, dark: Bool = false) -> Signal { - return postbox.transaction { transaction -> Peer? in - return transaction.getPeer(peerId) - } |> mapToSignal { peer -> Signal in - guard let peer = peer, let inputChannel = apiInputChannel(peer) else { +private func requestChannelStats(postbox: Postbox, network: Network, peerId: PeerId, dark: Bool = false) -> Signal { + return postbox.transaction { transaction -> (Int32, Peer)? in + if let peer = transaction.getPeer(peerId), let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData { + return (cachedData.statsDatacenterId, peer) + } + return nil + } |> mapToSignal { data -> Signal in + guard let (statsDatacenterId, peer) = data, let inputChannel = apiInputChannel(peer) else { return .never() } @@ -194,8 +266,8 @@ private func requestChannelStats(postbox: Postbox, network: Network, datacenterI let request = Api.functions.stats.getBroadcastStats(flags: flags, channel: inputChannel) let signal: Signal - if network.datacenterId != datacenterId { - signal = network.download(datacenterId: Int(datacenterId), isMedia: false, tag: nil) + if network.datacenterId != statsDatacenterId { + signal = network.download(datacenterId: Int(statsDatacenterId), isMedia: false, tag: nil) |> castError(MTRpcError.self) |> mapToSignal { worker in return worker.request(request) @@ -212,20 +284,33 @@ private func requestChannelStats(postbox: Postbox, network: Network, datacenterI } } -func requestGraph(network: Network, datacenterId: Int32, token: String, x: Int64? = nil) -> Signal { +func requestGraph(postbox: Postbox, network: Network, peerId: EnginePeer.Id, token: String, x: Int64? = nil) -> Signal { var flags: Int32 = 0 if let _ = x { flags |= (1 << 0) } let signal: Signal - if network.datacenterId != datacenterId { - signal = network.download(datacenterId: Int(datacenterId), isMedia: false, tag: nil) - |> castError(MTRpcError.self) - |> mapToSignal { worker in - return worker.request(Api.functions.stats.loadAsyncGraph(flags: flags, token: token, x: x)) + signal = postbox.transaction { transaction -> Int32? in + if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData { + return cachedData.statsDatacenterId + } + return nil + } + |> castError(MTRpcError.self) + |> mapToSignal { statsDatacenterId in + if let statsDatacenterId = statsDatacenterId { + if network.datacenterId != statsDatacenterId { + return network.download(datacenterId: Int(statsDatacenterId), isMedia: false, tag: nil) + |> castError(MTRpcError.self) + |> mapToSignal { worker in + return worker.request(Api.functions.stats.loadAsyncGraph(flags: flags, token: token, x: x)) + } + } else { + return network.request(Api.functions.stats.loadAsyncGraph(flags: flags, token: token, x: x)) + } + } else { + return .complete() } - } else { - signal = network.request(Api.functions.stats.loadAsyncGraph(flags: flags, token: token, x: x)) } return signal @@ -240,7 +325,6 @@ func requestGraph(network: Network, datacenterId: Int32, token: String, x: Int64 private final class ChannelStatsContextImpl { private let postbox: Postbox private let network: Network - private let datacenterId: Int32 private let peerId: PeerId private var _state: ChannelStatsContextState { @@ -258,12 +342,11 @@ private final class ChannelStatsContextImpl { private let disposable = MetaDisposable() private let disposables = DisposableDict() - init(postbox: Postbox, network: Network, datacenterId: Int32, peerId: PeerId) { + init(postbox: Postbox, network: Network, peerId: PeerId) { assert(Queue.mainQueue().isCurrent()) self.postbox = postbox self.network = network - self.datacenterId = datacenterId self.peerId = peerId self._state = ChannelStatsContextState(stats: nil) self._statePromise.set(.single(self._state)) @@ -280,7 +363,7 @@ private final class ChannelStatsContextImpl { private func load() { assert(Queue.mainQueue().isCurrent()) - self.disposable.set((requestChannelStats(postbox: self.postbox, network: self.network, datacenterId: self.datacenterId, peerId: self.peerId) + self.disposable.set((requestChannelStats(postbox: self.postbox, network: self.network, peerId: self.peerId) |> deliverOnMainQueue).start(next: { [weak self] stats in if let strongSelf = self { strongSelf._state = ChannelStatsContextState(stats: stats) @@ -294,7 +377,7 @@ private final class ChannelStatsContextImpl { return } if case let .OnDemand(token) = stats.growthGraph { - self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) |> deliverOnMainQueue).start(next: { [weak self] graph in if let strongSelf = self, let graph = graph { strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedGrowthGraph(graph)) @@ -309,7 +392,7 @@ private final class ChannelStatsContextImpl { return } if case let .OnDemand(token) = stats.followersGraph { - self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) |> deliverOnMainQueue).start(next: { [weak self] graph in if let strongSelf = self, let graph = graph { strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedFollowersGraph(graph)) @@ -324,7 +407,7 @@ private final class ChannelStatsContextImpl { return } if case let .OnDemand(token) = stats.muteGraph { - self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) |> deliverOnMainQueue).start(next: { [weak self] graph in if let strongSelf = self, let graph = graph { strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedMuteGraph(graph)) @@ -339,7 +422,7 @@ private final class ChannelStatsContextImpl { return } if case let .OnDemand(token) = stats.topHoursGraph { - self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) |> deliverOnMainQueue).start(next: { [weak self] graph in if let strongSelf = self, let graph = graph { strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedTopHoursGraph(graph)) @@ -354,7 +437,7 @@ private final class ChannelStatsContextImpl { return } if case let .OnDemand(token) = stats.interactionsGraph { - self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) |> deliverOnMainQueue).start(next: { [weak self] graph in if let strongSelf = self, let graph = graph { strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedInteractionsGraph(graph)) @@ -369,7 +452,7 @@ private final class ChannelStatsContextImpl { return } if case let .OnDemand(token) = stats.instantPageInteractionsGraph { - self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) |> deliverOnMainQueue).start(next: { [weak self] graph in if let strongSelf = self, let graph = graph { strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedInstantPageInteractionsGraph(graph)) @@ -384,7 +467,7 @@ private final class ChannelStatsContextImpl { return } if case let .OnDemand(token) = stats.viewsBySourceGraph { - self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) |> deliverOnMainQueue).start(next: { [weak self] graph in if let strongSelf = self, let graph = graph { strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedViewsBySourceGraph(graph)) @@ -399,7 +482,7 @@ private final class ChannelStatsContextImpl { return } if case let .OnDemand(token) = stats.newFollowersBySourceGraph { - self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) |> deliverOnMainQueue).start(next: { [weak self] graph in if let strongSelf = self, let graph = graph { strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedNewFollowersBySourceGraph(graph)) @@ -414,7 +497,7 @@ private final class ChannelStatsContextImpl { return } if case let .OnDemand(token) = stats.languagesGraph { - self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) |> deliverOnMainQueue).start(next: { [weak self] graph in if let strongSelf = self, let graph = graph { strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedLanguagesGraph(graph)) @@ -424,9 +507,54 @@ private final class ChannelStatsContextImpl { } } + func loadReactionsByEmotionGraph() { + guard let stats = self._state.stats else { + return + } + if case let .OnDemand(token) = stats.reactionsByEmotionGraph { + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) + |> deliverOnMainQueue).start(next: { [weak self] graph in + if let strongSelf = self, let graph = graph { + strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedReactionsByEmotionGraph(graph)) + strongSelf._statePromise.set(.single(strongSelf._state)) + } + }), forKey: token) + } + } + + func loadStoryInteractionsGraph() { + guard let stats = self._state.stats else { + return + } + if case let .OnDemand(token) = stats.storyInteractionsGraph { + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) + |> deliverOnMainQueue).start(next: { [weak self] graph in + if let strongSelf = self, let graph = graph { + strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedStoryInteractionsGraph(graph)) + strongSelf._statePromise.set(.single(strongSelf._state)) + } + }), forKey: token) + } + } + + func loadStoryReactionsByEmotionGraph() { + guard let stats = self._state.stats else { + return + } + if case let .OnDemand(token) = stats.storyReactionsByEmotionGraph { + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) + |> deliverOnMainQueue).start(next: { [weak self] graph in + if let strongSelf = self, let graph = graph { + strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedStoryReactionsByEmotionGraph(graph)) + strongSelf._statePromise.set(.single(strongSelf._state)) + } + }), forKey: token) + } + } + func loadDetailedGraph(_ graph: StatsGraph, x: Int64) -> Signal { if let token = graph.token { - return requestGraph(network: self.network, datacenterId: self.datacenterId, token: token, x: x) + return requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token, x: x) } else { return .single(nil) } @@ -448,9 +576,9 @@ public final class ChannelStatsContext { } } - public init(postbox: Postbox, network: Network, datacenterId: Int32, peerId: PeerId) { + public init(postbox: Postbox, network: Network, peerId: PeerId) { self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: { - return ChannelStatsContextImpl(postbox: postbox, network: network, datacenterId: datacenterId, peerId: peerId) + return ChannelStatsContextImpl(postbox: postbox, network: network, peerId: peerId) }) } @@ -507,6 +635,21 @@ public final class ChannelStatsContext { impl.loadLanguagesGraph() } } + public func loadReactionsByEmotionGraph() { + self.impl.with { impl in + impl.loadReactionsByEmotionGraph() + } + } + public func loadStoryInteractionsGraph() { + self.impl.with { impl in + impl.loadStoryInteractionsGraph() + } + } + public func loadStoryReactionsByEmotionGraph() { + self.impl.with { impl in + impl.loadStoryReactionsByEmotionGraph() + } + } public func loadDetailedGraph(_ graph: StatsGraph, x: Int64) -> Signal { return Signal { subscriber in @@ -540,7 +683,7 @@ public struct GroupStatsTopInviter: Equatable { public let inviteCount: Int32 } -public final class GroupStats: Equatable { +public struct GroupStats: Equatable { public let period: StatsDateRange public let members: StatsValue public let messages: StatsValue @@ -577,58 +720,6 @@ public final class GroupStats: Equatable { self.topInviters = topInviters } - public static func == (lhs: GroupStats, rhs: GroupStats) -> Bool { - if lhs.period != rhs.period { - return false - } - if lhs.members != rhs.members { - return false - } - if lhs.messages != rhs.messages { - return false - } - if lhs.viewers != rhs.viewers { - return false - } - if lhs.posters != rhs.posters { - return false - } - if lhs.growthGraph != rhs.growthGraph { - return false - } - if lhs.membersGraph != rhs.membersGraph { - return false - } - if lhs.newMembersBySourceGraph != rhs.newMembersBySourceGraph { - return false - } - if lhs.languagesGraph != rhs.languagesGraph { - return false - } - if lhs.messagesGraph != rhs.messagesGraph { - return false - } - if lhs.actionsGraph != rhs.actionsGraph { - return false - } - if lhs.topHoursGraph != rhs.topHoursGraph { - return false - } - if lhs.topWeekdaysGraph != rhs.topWeekdaysGraph { - return false - } - if lhs.topPosters != rhs.topPosters { - return false - } - if lhs.topAdmins != rhs.topAdmins { - return false - } - if lhs.topInviters != rhs.topInviters { - return false - } - return true - } - public func withUpdatedGrowthGraph(_ growthGraph: StatsGraph) -> GroupStats { return GroupStats(period: self.period, members: self.members, messages: self.messages, viewers: self.viewers, posters: self.posters, growthGraph: growthGraph, membersGraph: self.membersGraph, newMembersBySourceGraph: self.newMembersBySourceGraph, languagesGraph: self.languagesGraph, messagesGraph: self.messagesGraph, actionsGraph: self.actionsGraph, topHoursGraph: self.topHoursGraph, topWeekdaysGraph: self.topWeekdaysGraph, topPosters: self.topPosters, topAdmins: self.topAdmins, topInviters: self.topInviters) } @@ -666,11 +757,14 @@ public struct GroupStatsContextState: Equatable { public var stats: GroupStats? } -private func requestGroupStats(accountPeerId: PeerId, postbox: Postbox, network: Network, datacenterId: Int32, peerId: PeerId, dark: Bool = false) -> Signal { - return postbox.transaction { transaction -> Peer? in - return transaction.getPeer(peerId) - } |> mapToSignal { peer -> Signal in - guard let peer = peer, let inputChannel = apiInputChannel(peer) else { +private func requestGroupStats(accountPeerId: PeerId, postbox: Postbox, network: Network, peerId: PeerId, dark: Bool = false) -> Signal { + return postbox.transaction { transaction -> (Int32, Peer)? in + if let peer = transaction.getPeer(peerId), let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData { + return (cachedData.statsDatacenterId, peer) + } + return nil + } |> mapToSignal { data -> Signal in + guard let (statsDatacenterId, peer) = data, let inputChannel = apiInputChannel(peer) else { return .never() } @@ -680,8 +774,8 @@ private func requestGroupStats(accountPeerId: PeerId, postbox: Postbox, network: } let signal: Signal - if network.datacenterId != datacenterId { - signal = network.download(datacenterId: Int(datacenterId), isMedia: false, tag: nil) + if network.datacenterId != statsDatacenterId { + signal = network.download(datacenterId: Int(statsDatacenterId), isMedia: false, tag: nil) |> castError(MTRpcError.self) |> mapToSignal { worker in return worker.request(Api.functions.stats.getMegagroupStats(flags: flags, channel: inputChannel)) @@ -708,7 +802,6 @@ private final class GroupStatsContextImpl { private let postbox: Postbox private let network: Network private let accountPeerId: PeerId - private let datacenterId: Int32 private let peerId: PeerId private var _state: GroupStatsContextState { @@ -726,13 +819,12 @@ private final class GroupStatsContextImpl { private let disposable = MetaDisposable() private let disposables = DisposableDict() - init(postbox: Postbox, network: Network, accountPeerId: PeerId, datacenterId: Int32, peerId: PeerId) { + init(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId) { assert(Queue.mainQueue().isCurrent()) self.postbox = postbox self.network = network self.accountPeerId = accountPeerId - self.datacenterId = datacenterId self.peerId = peerId self._state = GroupStatsContextState(stats: nil) self._statePromise.set(.single(self._state)) @@ -749,7 +841,7 @@ private final class GroupStatsContextImpl { private func load() { assert(Queue.mainQueue().isCurrent()) - self.disposable.set((requestGroupStats(accountPeerId: self.accountPeerId, postbox: self.postbox, network: self.network, datacenterId: self.datacenterId, peerId: self.peerId) + self.disposable.set((requestGroupStats(accountPeerId: self.accountPeerId, postbox: self.postbox, network: self.network, peerId: self.peerId) |> deliverOnMainQueue).start(next: { [weak self] stats in if let strongSelf = self { strongSelf._state = GroupStatsContextState(stats: stats) @@ -763,7 +855,7 @@ private final class GroupStatsContextImpl { return } if case let .OnDemand(token) = stats.growthGraph { - self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) |> deliverOnMainQueue).start(next: { [weak self] graph in if let strongSelf = self, let graph = graph { strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedGrowthGraph(graph)) @@ -778,7 +870,7 @@ private final class GroupStatsContextImpl { return } if case let .OnDemand(token) = stats.membersGraph { - self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) |> deliverOnMainQueue).start(next: { [weak self] graph in if let strongSelf = self, let graph = graph { strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedMembersGraph(graph)) @@ -793,7 +885,7 @@ private final class GroupStatsContextImpl { return } if case let .OnDemand(token) = stats.newMembersBySourceGraph { - self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) |> deliverOnMainQueue).start(next: { [weak self] graph in if let strongSelf = self, let graph = graph { strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedNewMembersBySourceGraph(graph)) @@ -808,7 +900,7 @@ private final class GroupStatsContextImpl { return } if case let .OnDemand(token) = stats.languagesGraph { - self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) |> deliverOnMainQueue).start(next: { [weak self] graph in if let strongSelf = self, let graph = graph { strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedLanguagesGraph(graph)) @@ -823,7 +915,7 @@ private final class GroupStatsContextImpl { return } if case let .OnDemand(token) = stats.messagesGraph { - self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) |> deliverOnMainQueue).start(next: { [weak self] graph in if let strongSelf = self, let graph = graph { strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedMessagesGraph(graph)) @@ -838,7 +930,7 @@ private final class GroupStatsContextImpl { return } if case let .OnDemand(token) = stats.actionsGraph { - self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) |> deliverOnMainQueue).start(next: { [weak self] graph in if let strongSelf = self, let graph = graph { strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedActionsGraph(graph)) @@ -853,7 +945,7 @@ private final class GroupStatsContextImpl { return } if case let .OnDemand(token) = stats.topHoursGraph { - self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) |> deliverOnMainQueue).start(next: { [weak self] graph in if let strongSelf = self, let graph = graph { strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedTopHoursGraph(graph)) @@ -868,7 +960,7 @@ private final class GroupStatsContextImpl { return } if case let .OnDemand(token) = stats.topWeekdaysGraph { - self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) + self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token) |> deliverOnMainQueue).start(next: { [weak self] graph in if let strongSelf = self, let graph = graph { strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedTopWeekdaysGraph(graph)) @@ -880,7 +972,7 @@ private final class GroupStatsContextImpl { func loadDetailedGraph(_ graph: StatsGraph, x: Int64) -> Signal { if let token = graph.token { - return requestGraph(network: self.network, datacenterId: self.datacenterId, token: token, x: x) + return requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token, x: x) } else { return .single(nil) } @@ -902,9 +994,9 @@ public final class GroupStatsContext { } } - public init(postbox: Postbox, network: Network, accountPeerId: PeerId, datacenterId: Int32, peerId: PeerId) { + public init(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId) { self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: { - return GroupStatsContextImpl(postbox: postbox, network: network, accountPeerId: accountPeerId, datacenterId: datacenterId, peerId: peerId) + return GroupStatsContextImpl(postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId) }) } @@ -1035,23 +1127,47 @@ extension StatsPercentValue { } } -extension ChannelStatsMessageInteractions { - init(apiMessageInteractionCounters: Api.MessageInteractionCounters, peerId: PeerId) { - switch apiMessageInteractionCounters { - case let .messageInteractionCounters(msgId, views, forwards): - self = ChannelStatsMessageInteractions(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: msgId), views: views, forwards: forwards) +extension ChannelStatsPostInteractions { + init(apiPostInteractionCounters: Api.PostInteractionCounters, peerId: PeerId) { + switch apiPostInteractionCounters { + case let .postInteractionCountersMessage(msgId, views, forwards, reactions): + self = ChannelStatsPostInteractions(postId: .message(id: EngineMessage.Id(peerId: peerId, namespace: Namespaces.Message.Cloud, id: msgId)), views: views, forwards: forwards, reactions: reactions) + case let .postInteractionCountersStory(storyId, views, forwards, reactions): + self = ChannelStatsPostInteractions(postId: .story(peerId: peerId, id: storyId), views: views, forwards: forwards, reactions: reactions) } } } extension ChannelStats { - convenience init(apiBroadcastStats: Api.stats.BroadcastStats, peerId: PeerId) { + init(apiBroadcastStats: Api.stats.BroadcastStats, peerId: PeerId) { switch apiBroadcastStats { - case let .broadcastStats(period, followers, viewsPerPost, sharesPerPost, enabledNotifications, apiGrowthGraph, apiFollowersGraph, apiMuteGraph, apiTopHoursGraph, apiInteractionsGraph, apiInstantViewInteractionsGraph, apiViewsBySourceGraph, apiNewFollowersBySourceGraph, apiLanguagesGraph, recentMessageInteractions): - let growthGraph = StatsGraph(apiStatsGraph: apiGrowthGraph) - let isEmpty = growthGraph.isEmpty - - self.init(period: StatsDateRange(apiStatsDateRangeDays: period), followers: StatsValue(apiStatsAbsValueAndPrev: followers), viewsPerPost: StatsValue(apiStatsAbsValueAndPrev: viewsPerPost), sharesPerPost: StatsValue(apiStatsAbsValueAndPrev: sharesPerPost), enabledNotifications: StatsPercentValue(apiPercentValue: enabledNotifications), growthGraph: growthGraph, followersGraph: StatsGraph(apiStatsGraph: apiFollowersGraph), muteGraph: StatsGraph(apiStatsGraph: apiMuteGraph), topHoursGraph: StatsGraph(apiStatsGraph: apiTopHoursGraph), interactionsGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiInteractionsGraph), instantPageInteractionsGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiInstantViewInteractionsGraph), viewsBySourceGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiViewsBySourceGraph), newFollowersBySourceGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiNewFollowersBySourceGraph), languagesGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiLanguagesGraph), messageInteractions: recentMessageInteractions.map { ChannelStatsMessageInteractions(apiMessageInteractionCounters: $0, peerId: peerId) }) + case let .broadcastStats(period, followers, viewsPerPost, sharesPerPost, reactionsPerPost, viewsPerStory, sharesPerStory, reactionsPerStory, enabledNotifications, apiGrowthGraph, apiFollowersGraph, apiMuteGraph, apiTopHoursGraph, apiInteractionsGraph, apiInstantViewInteractionsGraph, apiViewsBySourceGraph, apiNewFollowersBySourceGraph, apiLanguagesGraph, apiReactionsByEmotionGraph, apiStoryInteractionsGraph, apiStoryReactionsByEmotionGraph, recentPostInteractions): + let growthGraph = StatsGraph(apiStatsGraph: apiGrowthGraph) + let isEmpty = growthGraph.isEmpty + + self.init( + period: StatsDateRange(apiStatsDateRangeDays: period), + followers: StatsValue(apiStatsAbsValueAndPrev: followers), + viewsPerPost: StatsValue(apiStatsAbsValueAndPrev: viewsPerPost), + sharesPerPost: StatsValue(apiStatsAbsValueAndPrev: sharesPerPost), + reactionsPerPost: StatsValue(apiStatsAbsValueAndPrev: reactionsPerPost), + viewsPerStory: StatsValue(apiStatsAbsValueAndPrev: viewsPerStory), + sharesPerStory: StatsValue(apiStatsAbsValueAndPrev: sharesPerStory), + reactionsPerStory: StatsValue(apiStatsAbsValueAndPrev: reactionsPerStory), + enabledNotifications: StatsPercentValue(apiPercentValue: enabledNotifications), + growthGraph: growthGraph, + followersGraph: StatsGraph(apiStatsGraph: apiFollowersGraph), + muteGraph: StatsGraph(apiStatsGraph: apiMuteGraph), + topHoursGraph: StatsGraph(apiStatsGraph: apiTopHoursGraph), + interactionsGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiInteractionsGraph), + instantPageInteractionsGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiInstantViewInteractionsGraph), + viewsBySourceGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiViewsBySourceGraph), + newFollowersBySourceGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiNewFollowersBySourceGraph), + languagesGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiLanguagesGraph), + reactionsByEmotionGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiReactionsByEmotionGraph), + storyInteractionsGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiStoryInteractionsGraph), + storyReactionsByEmotionGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiStoryReactionsByEmotionGraph), + postInteractions: recentPostInteractions.map { ChannelStatsPostInteractions(apiPostInteractionCounters: $0, peerId: peerId) }) } } } @@ -1084,7 +1200,7 @@ extension GroupStatsTopInviter { } extension GroupStats { - convenience init(apiMegagroupStats: Api.stats.MegagroupStats) { + init(apiMegagroupStats: Api.stats.MegagroupStats) { switch apiMegagroupStats { case let .megagroupStats(period, members, messages, viewers, posters, apiGrowthGraph, apiMembersGraph, apiNewMembersBySourceGraph, apiLanguagesGraph, apiMessagesGraph, apiActionsGraph, apiTopHoursGraph, apiTopWeekdaysGraph, topPosters, topAdmins, topInviters, _): let growthGraph = StatsGraph(apiStatsGraph: apiGrowthGraph) diff --git a/submodules/TelegramCore/Sources/Statistics/StoryStatistics.swift b/submodules/TelegramCore/Sources/Statistics/StoryStatistics.swift new file mode 100644 index 00000000000..4ef959885a1 --- /dev/null +++ b/submodules/TelegramCore/Sources/Statistics/StoryStatistics.swift @@ -0,0 +1,410 @@ +import Foundation +import SwiftSignalKit +import Postbox +import TelegramApi +import MtProtoKit + +public struct StoryStats: Equatable { + public let interactionsGraph: StatsGraph + public let interactionsGraphDelta: Int64 + public let reactionsGraph: StatsGraph + + init(interactionsGraph: StatsGraph, interactionsGraphDelta: Int64, reactionsGraph: StatsGraph) { + self.interactionsGraph = interactionsGraph + self.interactionsGraphDelta = interactionsGraphDelta + self.reactionsGraph = reactionsGraph + } + + public static func == (lhs: StoryStats, rhs: StoryStats) -> Bool { + if lhs.interactionsGraph != rhs.interactionsGraph { + return false + } + if lhs.interactionsGraphDelta != rhs.interactionsGraphDelta { + return false + } + if lhs.reactionsGraph != rhs.reactionsGraph { + return false + } + return true + } + + public func withUpdatedInteractionsGraph(_ interactionsGraph: StatsGraph) -> StoryStats { + return StoryStats(interactionsGraph: interactionsGraph, interactionsGraphDelta: self.interactionsGraphDelta, reactionsGraph: self.reactionsGraph) + } +} + +public struct StoryStatsContextState: Equatable { + public var stats: StoryStats? +} + +private func requestStoryStats(accountPeerId: PeerId, postbox: Postbox, network: Network, peerId: EnginePeer.Id, storyId: Int32, dark: Bool = false) -> Signal { + return postbox.transaction { transaction -> (Int32, Peer)? in + if let peer = transaction.getPeer(peerId), let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData { + return (cachedData.statsDatacenterId, peer) + } else { + return nil + } + } + |> mapToSignal { data -> Signal in + guard let (statsDatacenterId, peer) = data, let inputPeer = apiInputPeer(peer) else { + return .never() + } + var flags: Int32 = 0 + if dark { + flags |= (1 << 1) + } + + let request = Api.functions.stats.getStoryStats(flags: flags, peer: inputPeer, id: storyId) + let signal: Signal + if network.datacenterId != statsDatacenterId { + signal = network.download(datacenterId: Int(statsDatacenterId), isMedia: false, tag: nil) + |> castError(MTRpcError.self) + |> mapToSignal { worker in + return worker.request(request) + } + } else { + signal = network.request(request) + } + + return signal + |> mapToSignal { result -> Signal in + if case let .storyStats(apiInteractionsGraph, apiReactionsGraph) = result { + let interactionsGraph = StatsGraph(apiStatsGraph: apiInteractionsGraph) + var interactionsGraphDelta: Int64 = 86400 + if case let .Loaded(_, data) = interactionsGraph { + if let start = data.range(of: "[\"x\",") { + let substring = data.suffix(from: start.upperBound) + if let end = substring.range(of: "],") { + let valuesString = substring.prefix(through: substring.index(before: end.lowerBound)) + let values = valuesString.components(separatedBy: ",").compactMap { Int64($0) } + if values.count > 1 { + let first = values[0] + let second = values[1] + let delta = abs(second - first) / 1000 + interactionsGraphDelta = delta + } + } + } + } + let reactionsGraph = StatsGraph(apiStatsGraph: apiReactionsGraph) + return .single(StoryStats( + interactionsGraph: interactionsGraph, + interactionsGraphDelta: interactionsGraphDelta, + reactionsGraph: reactionsGraph + )) + } else { + return .single(nil) + } + } + |> retryRequest + } +} + +private final class StoryStatsContextImpl { + private let accountPeerId: EnginePeer.Id + private let postbox: Postbox + private let network: Network + private let peerId: EnginePeer.Id + private let storyId: Int32 + + private var _state: StoryStatsContextState { + didSet { + if self._state != oldValue { + self._statePromise.set(.single(self._state)) + } + } + } + private let _statePromise = Promise() + var state: Signal { + return self._statePromise.get() + } + + private let disposable = MetaDisposable() + private let disposables = DisposableDict() + + init(accountPeerId: EnginePeer.Id, postbox: Postbox, network: Network, peerId: EnginePeer.Id, storyId: Int32) { + assert(Queue.mainQueue().isCurrent()) + + self.accountPeerId = accountPeerId + self.postbox = postbox + self.network = network + self.peerId = peerId + self.storyId = storyId + self._state = StoryStatsContextState(stats: nil) + self._statePromise.set(.single(self._state)) + + self.load() + } + + deinit { + assert(Queue.mainQueue().isCurrent()) + self.disposable.dispose() + self.disposables.dispose() + } + + private func load() { + assert(Queue.mainQueue().isCurrent()) + + self.disposable.set((requestStoryStats(accountPeerId: self.accountPeerId, postbox: self.postbox, network: self.network, peerId: self.peerId, storyId: self.storyId) + |> deliverOnMainQueue).start(next: { [weak self] stats in + if let strongSelf = self { + strongSelf._state = StoryStatsContextState(stats: stats) + strongSelf._statePromise.set(.single(strongSelf._state)) + } + })) + } + + func loadDetailedGraph(_ graph: StatsGraph, x: Int64) -> Signal { + if let token = graph.token { + return requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token, x: x) + } else { + return .single(nil) + } + } +} + +public final class StoryStatsContext { + private let impl: QueueLocalObject + + public var state: Signal { + return Signal { subscriber in + let disposable = MetaDisposable() + self.impl.with { impl in + disposable.set(impl.state.start(next: { value in + subscriber.putNext(value) + })) + } + return disposable + } + } + + public init(account: Account, peerId: EnginePeer.Id, storyId: Int32) { + self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: { + return StoryStatsContextImpl(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, peerId: peerId, storyId: storyId) + }) + } + + public func loadDetailedGraph(_ graph: StatsGraph, x: Int64) -> Signal { + return Signal { subscriber in + let disposable = MetaDisposable() + self.impl.with { impl in + disposable.set(impl.loadDetailedGraph(graph, x: x).start(next: { value in + subscriber.putNext(value) + subscriber.putCompletion() + })) + } + return disposable + } + } +} + +private final class StoryStatsPublicForwardsContextImpl { + private let queue: Queue + private let account: Account + private let peerId: EnginePeer.Id + private let storyId: Int32 + private let disposable = MetaDisposable() + private var isLoadingMore: Bool = false + private var hasLoadedOnce: Bool = false + private var canLoadMore: Bool = true + private var results: [StoryStatsPublicForwardsContext.State.Forward] = [] + private var count: Int32 + private var lastOffset: String? + + let state = Promise() + + init(queue: Queue, account: Account, peerId: EnginePeer.Id, storyId: Int32) { + self.queue = queue + self.account = account + self.peerId = peerId + self.storyId = storyId + + self.count = 0 + + self.loadMore() + } + + deinit { + self.disposable.dispose() + } + + func reload() { + self.loadMore() + } + + func loadMore() { + if self.isLoadingMore || !self.canLoadMore { + return + } + self.isLoadingMore = true + let account = self.account + let accountPeerId = account.peerId + let peerId = self.peerId + let storyId = self.storyId + let lastOffset = self.lastOffset + + self.disposable.set((self.account.postbox.transaction { transaction -> Api.InputPeer? in + return transaction.getPeer(peerId).flatMap(apiInputPeer) + } + |> mapToSignal { inputPeer -> Signal<([StoryStatsPublicForwardsContext.State.Forward], Int32, String?), NoError> in + if let inputPeer = inputPeer { + let offset = lastOffset ?? "" + let signal = account.network.request(Api.functions.stats.getStoryPublicForwards(peer: inputPeer, id: storyId, offset: offset, limit: 50)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal<([StoryStatsPublicForwardsContext.State.Forward], Int32, String?), NoError> in + return account.postbox.transaction { transaction -> ([StoryStatsPublicForwardsContext.State.Forward], Int32, String?) in + guard let result = result else { + return ([], 0, nil) + } + switch result { + case let .publicForwards(_, count, forwards, nextOffset, chats, users): + var peers: [PeerId: Peer] = [:] + for user in users { + if let user = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) { + peers[user.id] = user + } + } + for chat in chats { + if let groupOrChannel = mergeGroupOrChannel(lhs: transaction.getPeer(chat.peerId), rhs: chat) { + peers[groupOrChannel.id] = groupOrChannel + } + } + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) + var resultForwards: [StoryStatsPublicForwardsContext.State.Forward] = [] + for forward in forwards { + switch forward { + case let .publicForwardMessage(apiMessage): + if let message = StoreMessage(apiMessage: apiMessage, peerIsForum: false), let renderedMessage = locallyRenderedMessage(message: message, peers: peers) { + resultForwards.append(.message(EngineMessage(renderedMessage))) + } + case let .publicForwardStory(apiPeer, apiStory): + + if let storedItem = Stories.StoredItem(apiStoryItem: apiStory, peerId: apiPeer.peerId, transaction: transaction), case let .item(item) = storedItem, let media = item.media, let peer = peers[apiPeer.peerId] { + let mappedItem = EngineStoryItem( + id: item.id, + timestamp: item.timestamp, + expirationTimestamp: item.expirationTimestamp, + media: EngineMedia(media), + mediaAreas: item.mediaAreas, + text: item.text, + entities: item.entities, + views: item.views.flatMap { views in + return EngineStoryItem.Views( + seenCount: views.seenCount, + reactedCount: views.reactedCount, + forwardCount: views.forwardCount, + seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in + return transaction.getPeer(id).flatMap(EnginePeer.init) + }, + reactions: views.reactions, + hasList: views.hasList + ) + }, + privacy: item.privacy.flatMap(EngineStoryPrivacy.init), + isPinned: item.isPinned, + isExpired: item.isExpired, + isPublic: item.isPublic, + isPending: false, + isCloseFriends: item.isCloseFriends, + isContacts: item.isContacts, + isSelectedContacts: item.isSelectedContacts, + isForwardingDisabled: item.isForwardingDisabled, + isEdited: item.isEdited, + isMy: item.isMy, + myReaction: item.myReaction, + forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) } + ) + resultForwards.append(.story(EnginePeer(peer), mappedItem)) + } + } + } + return (resultForwards, count, nextOffset) + } + } + } + return signal + } else { + return .single(([], 0, nil)) + } + } + |> deliverOn(self.queue)).start(next: { [weak self] forwards, updatedCount, nextOffset in + guard let strongSelf = self else { + return + } + strongSelf.lastOffset = nextOffset + for forwards in forwards { + strongSelf.results.append(forwards) + } + strongSelf.isLoadingMore = false + strongSelf.hasLoadedOnce = true + strongSelf.canLoadMore = !forwards.isEmpty && nextOffset != nil + if strongSelf.canLoadMore { + strongSelf.count = max(updatedCount, Int32(strongSelf.results.count)) + } else { + strongSelf.count = Int32(strongSelf.results.count) + } + strongSelf.updateState() + })) + self.updateState() + } + + private func updateState() { + self.state.set(.single(StoryStatsPublicForwardsContext.State(forwards: self.results, isLoadingMore: self.isLoadingMore, hasLoadedOnce: self.hasLoadedOnce, canLoadMore: self.canLoadMore, count: self.count))) + } +} + +public final class StoryStatsPublicForwardsContext { + public struct State: Equatable { + public enum Forward: Equatable { + case message(EngineMessage) + case story(EnginePeer, EngineStoryItem) + } + public var forwards: [Forward] + public var isLoadingMore: Bool + public var hasLoadedOnce: Bool + public var canLoadMore: Bool + public var count: Int32 + + public static var Empty = State(forwards: [], isLoadingMore: false, hasLoadedOnce: true, canLoadMore: false, count: 0) + public static var Loading = State(forwards: [], isLoadingMore: false, hasLoadedOnce: false, canLoadMore: false, count: 0) + } + + + private let queue: Queue = Queue() + private let impl: QueueLocalObject + + public var state: Signal { + return Signal { subscriber in + let disposable = MetaDisposable() + self.impl.with { impl in + disposable.set(impl.state.get().start(next: { value in + subscriber.putNext(value) + })) + } + return disposable + } + } + + public init(account: Account, peerId: EnginePeer.Id, storyId: Int32) { + let queue = self.queue + self.impl = QueueLocalObject(queue: queue, generate: { + return StoryStatsPublicForwardsContextImpl(queue: queue, account: account, peerId: peerId, storyId: storyId) + }) + } + + public func loadMore() { + self.impl.with { impl in + impl.loadMore() + } + } + + public func reload() { + self.impl.with { impl in + impl.reload() + } + } +} + diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedChannelData.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedChannelData.swift index fb72b883215..4d86257d8ca 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedChannelData.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedChannelData.swift @@ -160,6 +160,14 @@ public struct PeerMembersHidden: Codable, Equatable { } } +private struct PeerViewForumAsMessages: Codable { + public var value: Bool + + public init(value: Bool) { + self.value = value + } +} + public final class CachedChannelData: CachedPeerData { public enum LinkedDiscussionPeerId: Equatable { case unknown @@ -250,6 +258,7 @@ public final class CachedChannelData: CachedPeerData { public let sendAsPeerId: PeerId? public let allowedReactions: EnginePeerCachedInfoItem public let membersHidden: EnginePeerCachedInfoItem + public let viewForumAsMessages: EnginePeerCachedInfoItem public let peerIds: Set public let messageIds: Set @@ -289,6 +298,7 @@ public final class CachedChannelData: CachedPeerData { self.sendAsPeerId = nil self.allowedReactions = .unknown self.membersHidden = .unknown + self.viewForumAsMessages = .unknown } public init( @@ -320,7 +330,8 @@ public final class CachedChannelData: CachedPeerData { inviteRequestsPending: Int32?, sendAsPeerId: PeerId?, allowedReactions: EnginePeerCachedInfoItem, - membersHidden: EnginePeerCachedInfoItem + membersHidden: EnginePeerCachedInfoItem, + viewForumAsMessages: EnginePeerCachedInfoItem ) { self.isNotAccessible = isNotAccessible self.flags = flags @@ -351,6 +362,7 @@ public final class CachedChannelData: CachedPeerData { self.sendAsPeerId = sendAsPeerId self.allowedReactions = allowedReactions self.membersHidden = membersHidden + self.viewForumAsMessages = viewForumAsMessages var peerIds = Set() for botInfo in botInfos { @@ -378,119 +390,123 @@ 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } public func withUpdatedAllowedReactions(_ allowedReactions: 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, allowedReactions: allowedReactions, membersHidden: self.membersHidden) + 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, allowedReactions: allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages) } 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, allowedReactions: self.allowedReactions, membersHidden: membersHidden) + 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, allowedReactions: self.allowedReactions, membersHidden: membersHidden, viewForumAsMessages: self.viewForumAsMessages) + } + + 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, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: viewForumAsMessages) } public init(decoder: PostboxDecoder) { @@ -594,6 +610,12 @@ public final class CachedChannelData: CachedPeerData { self.membersHidden = .unknown } + if let viewForumAsMessages = decoder.decode(PeerViewForumAsMessages.self, forKey: "viewForumAsMessages") { + self.viewForumAsMessages = .known(viewForumAsMessages.value) + } else { + self.viewForumAsMessages = .unknown + } + if case let .known(linkedDiscussionPeerIdValue) = self.linkedDiscussionPeerId { if let linkedDiscussionPeerIdValue = linkedDiscussionPeerIdValue { peerIds.insert(linkedDiscussionPeerIdValue) @@ -751,6 +773,13 @@ public final class CachedChannelData: CachedPeerData { case let .known(value): encoder.encode(value, forKey: "membersHidden") } + + switch self.viewForumAsMessages { + case .unknown: + encoder.encodeNil(forKey: "viewForumAsMessages") + case let .known(value): + encoder.encode(PeerViewForumAsMessages(value: value), forKey: "viewForumAsMessages") + } } public func isEqual(to: CachedPeerData) -> Bool { @@ -874,6 +903,10 @@ public final class CachedChannelData: CachedPeerData { return false } + if other.viewForumAsMessages != self.viewForumAsMessages { + return false + } + return true } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift index 3c308396b0a..0f83ef3cd64 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift @@ -113,6 +113,8 @@ public struct Namespaces { public static let storySendAsPeerIds: Int8 = 29 public static let cachedChannelBoosts: Int8 = 31 public static let displayedMessageNotifications: Int8 = 32 + public static let recommendedChannels: Int8 = 33 + public static let peerColorOptions: Int8 = 34 } public struct UnorderedItemList { @@ -268,6 +270,7 @@ private enum PreferencesKeyValues: Int32 { case chatListFilterUpdates = 30 case globalPrivacySettings = 31 case storiesConfiguration = 32 + case audioTranscriptionTrialState = 33 } public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey { @@ -432,6 +435,12 @@ public struct PreferencesKeys { key.setInt32(0, value: PreferencesKeyValues.storiesConfiguration.rawValue) return key }() + + public static let audioTranscriptionTrialState: ValueBoxKey = { + let key = ValueBoxKey(length: 4) + key.setInt32(0, value: PreferencesKeyValues.audioTranscriptionTrialState.rawValue) + return key + }() } private enum SharedDataKeyValues: Int32 { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_SecretChatOutgoingOperation.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_SecretChatOutgoingOperation.swift index 59931fec817..050176527e1 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_SecretChatOutgoingOperation.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_SecretChatOutgoingOperation.swift @@ -235,11 +235,12 @@ public struct StandaloneSecretMessageContents: Codable { return innerEncoder.makeData() } try container.encode(attributes, forKey: .attributes) - try container.encodeIfPresent(self.media.flatMap { media in + let data: Data? = self.media.flatMap { media in let innerEncoder = PostboxEncoder() innerEncoder.encodeRootObject(media) return innerEncoder.makeData() - }, forKey: .media) + } + try container.encodeIfPresent(data, forKey: .media) try container.encodeIfPresent(self.file, forKey: .file) } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift index cde89cff886..566c9ca0bdf 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift @@ -152,7 +152,7 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = { guard let data = entry.get(MessageHistoryThreadData.self) else { return nil } - return Message.AssociatedThreadInfo(title: data.info.title, icon: data.info.icon, iconColor: data.info.iconColor) + return Message.AssociatedThreadInfo(title: data.info.title, icon: data.info.icon, iconColor: data.info.iconColor, isClosed: data.isClosed) }, decodeAutoremoveTimeout: { cachedData in if let cachedData = cachedData as? CachedUserData { @@ -170,6 +170,14 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = { } return nil }, + decodeDisplayPeerAsRegularChat: { cachedData in + if let cachedData = cachedData as? CachedChannelData { + if case let .known(value) = cachedData.viewForumAsMessages { + return value + } + } + return false + }, 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 ba725bccd09..08abf2fdabe 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift @@ -169,6 +169,8 @@ public final class TelegramChannel: Peer, Equatable { public let storiesHidden: Bool? public let nameColor: PeerNameColor? public let backgroundEmojiId: Int64? + public let profileColor: PeerNameColor? + public let profileBackgroundEmojiId: Int64? public var indexName: PeerIndexNameRepresentation { var addressNames = self.usernames.map { $0.username } @@ -209,7 +211,9 @@ public final class TelegramChannel: Peer, Equatable { usernames: [TelegramPeerUsername], storiesHidden: Bool?, nameColor: PeerNameColor?, - backgroundEmojiId: Int64? + backgroundEmojiId: Int64?, + profileColor: PeerNameColor?, + profileBackgroundEmojiId: Int64? ) { self.id = id self.accessHash = accessHash @@ -229,6 +233,8 @@ public final class TelegramChannel: Peer, Equatable { self.storiesHidden = storiesHidden self.nameColor = nameColor self.backgroundEmojiId = backgroundEmojiId + self.profileColor = profileColor + self.profileBackgroundEmojiId = profileBackgroundEmojiId } public init(decoder: PostboxDecoder) { @@ -260,6 +266,8 @@ public final class TelegramChannel: Peer, Equatable { self.storiesHidden = decoder.decodeOptionalBoolForKey("sth") self.nameColor = decoder.decodeOptionalInt32ForKey("nclr").flatMap { PeerNameColor(rawValue: $0) } self.backgroundEmojiId = decoder.decodeOptionalInt64ForKey("bgem") + self.profileColor = decoder.decodeOptionalInt32ForKey("pclr").flatMap { PeerNameColor(rawValue: $0) } + self.profileBackgroundEmojiId = decoder.decodeOptionalInt64ForKey("pgem") } public func encode(_ encoder: PostboxEncoder) { @@ -327,6 +335,18 @@ public final class TelegramChannel: Peer, Equatable { } else { encoder.encodeNil(forKey: "bgem") } + + if let profileColor = self.profileColor { + encoder.encodeInt32(profileColor.rawValue, forKey: "pclr") + } else { + encoder.encodeNil(forKey: "pclr") + } + + if let profileBackgroundEmojiId = self.profileBackgroundEmojiId { + encoder.encodeInt64(profileBackgroundEmojiId, forKey: "pgem") + } else { + encoder.encodeNil(forKey: "pgem") + } } public func isEqual(_ other: Peer) -> Bool { @@ -373,35 +393,49 @@ public final class TelegramChannel: Peer, Equatable { if lhs.backgroundEmojiId != rhs.backgroundEmojiId { return false } + if lhs.profileColor != rhs.profileColor { + return false + } + if lhs.profileBackgroundEmojiId != rhs.profileBackgroundEmojiId { + 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) + 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) } 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) + 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) } 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) + 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) } 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) + 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) } 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) + 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) } 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) + 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) } 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) + 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) + } + + 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) + } + + 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) } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift index 23ed143d9e3..99216285a38 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift @@ -107,10 +107,12 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case suggestedProfilePhoto(image: TelegramMediaImage?) case attachMenuBotAllowed case requestedPeer(buttonId: Int32, peerId: PeerId) - case setChatWallpaper(wallpaper: TelegramWallpaper) + case setChatWallpaper(wallpaper: TelegramWallpaper, forBoth: Bool) case setSameChatWallpaper(wallpaper: TelegramWallpaper) case giftCode(slug: String, fromGiveaway: Bool, isUnclaimed: Bool, boostPeerId: PeerId?, months: Int32) case giveawayLaunched + case joinedChannel + case giveawayResults(winners: Int32, unclaimed: Int32) public init(decoder: PostboxDecoder) { let rawValue: Int32 = decoder.decodeInt32ForKey("_rawValue", orElse: 0) @@ -193,7 +195,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { self = .requestedPeer(buttonId: decoder.decodeInt32ForKey("b", orElse: 0), peerId: PeerId(decoder.decodeInt64ForKey("pi", orElse: 0))) case 33: if let wallpaper = decoder.decode(TelegramWallpaperNativeCodable.self, forKey: "wallpaper")?.value { - self = .setChatWallpaper(wallpaper: wallpaper) + self = .setChatWallpaper(wallpaper: wallpaper, forBoth: decoder.decodeBoolForKey("both", orElse: false)) } else { self = .unknown } @@ -209,6 +211,10 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { self = .giftCode(slug: decoder.decodeStringForKey("slug", orElse: ""), fromGiveaway: decoder.decodeBoolForKey("give", orElse: false), isUnclaimed: decoder.decodeBoolForKey("unclaimed", orElse: false), boostPeerId: PeerId(decoder.decodeInt64ForKey("pi", orElse: 0)), months: decoder.decodeInt32ForKey("months", orElse: 0)) case 37: self = .giveawayLaunched + case 38: + self = .joinedChannel + case 39: + self = .giveawayResults(winners: decoder.decodeInt32ForKey("winners", orElse: 0), unclaimed: decoder.decodeInt32ForKey("unclaimed", orElse: 0)) default: self = .unknown } @@ -370,9 +376,10 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { encoder.encodeInt32(32, forKey: "_rawValue") encoder.encodeInt32(buttonId, forKey: "b") encoder.encodeInt64(peerId.toInt64(), forKey: "pi") - case let .setChatWallpaper(wallpaper): + case let .setChatWallpaper(wallpaper, forBoth): encoder.encodeInt32(33, forKey: "_rawValue") encoder.encode(TelegramWallpaperNativeCodable(wallpaper), forKey: "wallpaper") + encoder.encodeBool(forBoth, forKey: "both") case let .setSameChatWallpaper(wallpaper): encoder.encodeInt32(34, forKey: "_rawValue") encoder.encode(TelegramWallpaperNativeCodable(wallpaper), forKey: "wallpaper") @@ -401,6 +408,12 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { encoder.encodeInt32(months, forKey: "months") case .giveawayLaunched: encoder.encodeInt32(37, forKey: "_rawValue") + case .joinedChannel: + encoder.encodeInt32(38, forKey: "_rawValue") + case let .giveawayResults(winners, unclaimed): + encoder.encodeInt32(39, forKey: "_rawValue") + encoder.encodeInt32(winners, forKey: "winners") + encoder.encodeInt32(unclaimed, forKey: "unclaimed") } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift index 714303790ca..81d610ef09a 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift @@ -112,6 +112,8 @@ public final class TelegramUser: Peer, Equatable { public let storiesHidden: Bool? public let nameColor: PeerNameColor? public let backgroundEmojiId: Int64? + public let profileColor: PeerNameColor? + public let profileBackgroundEmojiId: Int64? public var nameOrPhone: String { if let firstName = self.firstName { @@ -198,7 +200,9 @@ public final class TelegramUser: Peer, Equatable { usernames: [TelegramPeerUsername], storiesHidden: Bool?, nameColor: PeerNameColor?, - backgroundEmojiId: Int64? + backgroundEmojiId: Int64?, + profileColor: PeerNameColor?, + profileBackgroundEmojiId: Int64? ) { self.id = id self.accessHash = accessHash @@ -215,6 +219,8 @@ public final class TelegramUser: Peer, Equatable { self.storiesHidden = storiesHidden self.nameColor = nameColor self.backgroundEmojiId = backgroundEmojiId + self.profileColor = profileColor + self.profileBackgroundEmojiId = profileBackgroundEmojiId } public init(decoder: PostboxDecoder) { @@ -257,6 +263,8 @@ public final class TelegramUser: Peer, Equatable { self.nameColor = decoder.decodeOptionalInt32ForKey("nclr").flatMap { PeerNameColor(rawValue: $0) } self.backgroundEmojiId = decoder.decodeOptionalInt64ForKey("bgem") + self.profileColor = decoder.decodeOptionalInt32ForKey("pclr").flatMap { PeerNameColor(rawValue: $0) } + self.profileBackgroundEmojiId = decoder.decodeOptionalInt64ForKey("pgem") } public func encode(_ encoder: PostboxEncoder) { @@ -328,6 +336,18 @@ public final class TelegramUser: Peer, Equatable { } else { encoder.encodeNil(forKey: "bgem") } + + if let profileColor = self.profileColor { + encoder.encodeInt32(profileColor.rawValue, forKey: "pclr") + } else { + encoder.encodeNil(forKey: "pclr") + } + + if let profileBackgroundEmojiId = self.profileBackgroundEmojiId { + encoder.encodeInt64(profileBackgroundEmojiId, forKey: "pgem") + } else { + encoder.encodeNil(forKey: "pgem") + } } public func isEqual(_ other: Peer) -> Bool { @@ -389,47 +409,61 @@ public final class TelegramUser: Peer, Equatable { if lhs.backgroundEmojiId != rhs.backgroundEmojiId { return false } + if lhs.profileColor != rhs.profileColor { + return false + } + if lhs.profileBackgroundEmojiId != rhs.profileBackgroundEmojiId { + return false + } return true } public func withUpdatedUsername(_ username: String?) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) } public func withUpdatedUsernames(_ usernames: [TelegramPeerUsername]) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) } public func withUpdatedNames(firstName: String?, lastName: String?) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: firstName, lastName: lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: firstName, lastName: lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) } public func withUpdatedPhone(_ phone: String?) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) } public func withUpdatedPhoto(_ representations: [TelegramMediaImageRepresentation]) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: phone, photo: representations, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: phone, photo: representations, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) } public func withUpdatedEmojiStatus(_ emojiStatus: PeerEmojiStatus?) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) } public func withUpdatedFlags(_ flags: UserInfoFlags) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) } public func withUpdatedStoriesHidden(_ storiesHidden: Bool?) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) } public func withUpdatedNameColor(_ nameColor: PeerNameColor) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: nameColor, backgroundEmojiId: self.backgroundEmojiId) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) } public func withUpdatedBackgroundEmojiId(_ backgroundEmojiId: Int64?) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: backgroundEmojiId) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) + } + + public func withUpdatedProfileColor(_ profileColor: PeerNameColor?) -> TelegramUser { + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) + } + + public func withUpdatedProfileBackgroundEmojiId(_ profileBackgroundEmojiId: Int64?) -> TelegramUser { + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/ChangeAccountPhoneNumber.swift b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/ChangeAccountPhoneNumber.swift index 44d473bfd24..778b2e986fa 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/ChangeAccountPhoneNumber.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/ChangeAccountPhoneNumber.swift @@ -36,8 +36,21 @@ public enum RequestChangeAccountPhoneNumberVerificationError { case generic } -func _internal_requestChangeAccountPhoneNumberVerification(account: Account, phoneNumber: String) -> Signal { - return account.network.request(Api.functions.account.sendChangePhoneCode(phoneNumber: phoneNumber, settings: .codeSettings(flags: 0, logoutTokens: nil, token: nil, appSandbox: nil)), automaticFloodWait: false) +func _internal_requestChangeAccountPhoneNumberVerification(account: Account, phoneNumber: String, pushNotificationConfiguration: AuthorizationCodePushNotificationConfiguration?, firebaseSecretStream: Signal<[String: String], NoError>) -> Signal { + var flags: Int32 = 0 + + flags |= 1 << 5 //allowMissedCall + + var token: String? + var appSandbox: Api.Bool? + if let pushNotificationConfiguration = pushNotificationConfiguration { + flags |= 1 << 7 + flags |= 1 << 8 + token = pushNotificationConfiguration.token + appSandbox = pushNotificationConfiguration.isSandbox ? .boolTrue : .boolFalse + } + + return account.network.request(Api.functions.account.sendChangePhoneCode(phoneNumber: phoneNumber, settings: .codeSettings(flags: flags, logoutTokens: nil, token: token, appSandbox: appSandbox)), automaticFloodWait: false) |> mapError { error -> RequestChangeAccountPhoneNumberVerificationError in if error.errorDescription.hasPrefix("FLOOD_WAIT") { return .limitExceeded diff --git a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/TelegramEngineAccountData.swift b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/TelegramEngineAccountData.swift index b966accbeba..f6d798ebe75 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/TelegramEngineAccountData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/TelegramEngineAccountData.swift @@ -15,8 +15,8 @@ public extension TelegramEngine { return _internal_acceptTermsOfService(account: self.account, id: id) } - public func requestChangeAccountPhoneNumberVerification(phoneNumber: String) -> Signal { - return _internal_requestChangeAccountPhoneNumberVerification(account: self.account, phoneNumber: phoneNumber) + public func requestChangeAccountPhoneNumberVerification(phoneNumber: String, pushNotificationConfiguration: AuthorizationCodePushNotificationConfiguration?, firebaseSecretStream: Signal<[String: String], NoError>) -> Signal { + return _internal_requestChangeAccountPhoneNumberVerification(account: self.account, phoneNumber: phoneNumber, pushNotificationConfiguration: pushNotificationConfiguration, firebaseSecretStream: firebaseSecretStream) } public func requestNextChangeAccountPhoneNumberVerification(phoneNumber: String, phoneCodeHash: String) -> Signal { @@ -35,8 +35,12 @@ public extension TelegramEngine { return _internal_updateAbout(account: self.account, about: about) } - public func updateNameColorAndEmoji(nameColor: PeerNameColor, backgroundEmojiId: Int64?) -> Signal { - return _internal_updateNameColorAndEmoji(account: self.account, nameColor: nameColor, backgroundEmojiId: backgroundEmojiId) + public func observeAvailableColorOptions(scope: PeerColorsScope) -> Signal { + return _internal_observeAvailableColorOptions(postbox: self.account.postbox, scope: scope) + } + + public func updateNameColorAndEmoji(nameColor: PeerNameColor, backgroundEmojiId: Int64?, profileColor: PeerNameColor?, profileBackgroundEmojiId: Int64?) -> Signal { + return _internal_updateNameColorAndEmoji(account: self.account, nameColor: nameColor, backgroundEmojiId: backgroundEmojiId, profileColor: profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId) } public func unregisterNotificationToken(token: Data, type: NotificationTokenType, otherAccountUserIds: [PeerId.Id]) -> Signal { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/UpdateAccountPeerName.swift b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/UpdateAccountPeerName.swift index 13d45fb7822..48216dd0d10 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/UpdateAccountPeerName.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/UpdateAccountPeerName.swift @@ -51,13 +51,12 @@ public enum UpdateNameColorAndEmojiError { case generic } -func _internal_updateNameColorAndEmoji(account: Account, nameColor: PeerNameColor, backgroundEmojiId: Int64?) -> Signal { - let flags: Int32 = (1 << 0) +func _internal_updateNameColorAndEmoji(account: Account, nameColor: PeerNameColor, backgroundEmojiId: Int64?, profileColor: PeerNameColor?, profileBackgroundEmojiId: Int64?) -> Signal { return account.postbox.transaction { transaction -> Signal in guard let peer = transaction.getPeer(account.peerId) as? TelegramUser else { return .complete() } - updatePeersCustom(transaction: transaction, peers: [peer.withUpdatedNameColor(nameColor).withUpdatedBackgroundEmojiId(backgroundEmojiId)], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [peer.withUpdatedNameColor(nameColor).withUpdatedBackgroundEmojiId(backgroundEmojiId).withUpdatedProfileColor(profileColor).withUpdatedProfileBackgroundEmojiId(profileBackgroundEmojiId)], update: { _, updated in return updated }) return .single(peer) @@ -65,11 +64,21 @@ func _internal_updateNameColorAndEmoji(account: Account, nameColor: PeerNameColo |> switchToLatest |> castError(UpdateNameColorAndEmojiError.self) |> mapToSignal { _ -> Signal in - return account.network.request(Api.functions.account.updateColor(flags: flags, color: nameColor.rawValue, backgroundEmojiId: backgroundEmojiId ?? 0)) + let flagsReplies: Int32 = (1 << 0) | (1 << 2) + + var flagsProfile: Int32 = (1 << 0) | (1 << 1) + if profileColor != nil { + flagsProfile |= (1 << 2) + } + + return combineLatest( + account.network.request(Api.functions.account.updateColor(flags: flagsReplies, color: nameColor.rawValue, backgroundEmojiId: backgroundEmojiId ?? 0)), + account.network.request(Api.functions.account.updateColor(flags: flagsProfile, color: profileColor?.rawValue, backgroundEmojiId: profileBackgroundEmojiId ?? 0)) + ) |> mapError { _ -> UpdateNameColorAndEmojiError in return .generic } - |> mapToSignal { _ -> Signal in + |> mapToSignal { _, _ -> Signal in return .complete() } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift index 3d43d551d37..9e11ce4fca8 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift @@ -2254,7 +2254,7 @@ func _internal_groupCallDisplayAsAvailablePeers(accountPeerId: PeerId, network: for chat in chats { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { switch chat { - case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _): + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _): if let participantsCount = participantsCount { subscribers[groupOrChannel.id] = participantsCount } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/ConfigurationData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/ConfigurationData.swift index 48a83d7b9a4..03845767f65 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/ConfigurationData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/ConfigurationData.swift @@ -59,6 +59,7 @@ public enum EngineConfiguration { public let maxGiveawayChannelsCount: Int32 public let maxGiveawayCountriesCount: Int32 public let maxGiveawayPeriodSeconds: Int32 + public let maxChannelRecommendationsCount: Int32 public static var defaultValue: UserLimits { return UserLimits(UserLimitsConfiguration.defaultValue) @@ -87,7 +88,8 @@ public enum EngineConfiguration { maxStoriesSuggestedReactions: Int32, maxGiveawayChannelsCount: Int32, maxGiveawayCountriesCount: Int32, - maxGiveawayPeriodSeconds: Int32 + maxGiveawayPeriodSeconds: Int32, + maxChannelRecommendationsCount: Int32 ) { self.maxPinnedChatCount = maxPinnedChatCount self.maxArchivedPinnedChatCount = maxArchivedPinnedChatCount @@ -112,6 +114,7 @@ public enum EngineConfiguration { self.maxGiveawayChannelsCount = maxGiveawayChannelsCount self.maxGiveawayCountriesCount = maxGiveawayCountriesCount self.maxGiveawayPeriodSeconds = maxGiveawayPeriodSeconds + self.maxChannelRecommendationsCount = maxChannelRecommendationsCount } } } @@ -171,7 +174,8 @@ public extension EngineConfiguration.UserLimits { maxStoriesSuggestedReactions: userLimitsConfiguration.maxStoriesSuggestedReactions, maxGiveawayChannelsCount: userLimitsConfiguration.maxGiveawayChannelsCount, maxGiveawayCountriesCount: userLimitsConfiguration.maxGiveawayCountriesCount, - maxGiveawayPeriodSeconds: userLimitsConfiguration.maxGiveawayPeriodSeconds + maxGiveawayPeriodSeconds: userLimitsConfiguration.maxGiveawayPeriodSeconds, + maxChannelRecommendationsCount: userLimitsConfiguration.maxChannelRecommendationsCount ) } } @@ -502,5 +506,58 @@ public extension TelegramEngine.EngineData.Item { return value } } + + public struct AudioTranscriptionTrial: TelegramEngineDataItem, PostboxViewDataItem { + public typealias Result = AudioTranscription.TrialState + + public init() { + } + + var key: PostboxViewKey { + return .preferences(keys: Set([PreferencesKeys.audioTranscriptionTrialState])) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? PreferencesView else { + preconditionFailure() + } + guard let value = view.values[PreferencesKeys.audioTranscriptionTrialState]?.get(AudioTranscription.TrialState.self) else { + return AudioTranscription.TrialState.defaultValue + } + return value + } + } + + public struct AvailableColorOptions: TelegramEngineDataItem, PostboxViewDataItem { + public typealias Result = EngineAvailableColorOptions + + public let scope: PeerColorsScope + + public init(scope: PeerColorsScope) { + self.scope = scope + } + + var key: PostboxViewKey { + let key = ValueBoxKey(length: 8) + switch scope { + case .replies: + key.setInt64(0, value: 0) + case .profile: + key.setInt64(0, value: 1) + } + let viewKey: PostboxViewKey = .cachedItem(ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.peerColorOptions, key: key)) + return viewKey + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? CachedItemView else { + preconditionFailure() + } + guard let value = view.value?.get(EngineAvailableColorOptions.self) else { + return EngineAvailableColorOptions(hash: 0, options: []) + } + return value + } + } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift index acef2bc6714..493c924e6ce 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift @@ -15,6 +15,7 @@ private class AdMessagesHistoryContextImpl { case target case messageId case startParam + case buttonText case sponsorInfo case additionalInfo } @@ -33,6 +34,7 @@ private class AdMessagesHistoryContextImpl { case peer case invite case webPage + case botApp } struct Invite: Equatable, Codable { @@ -78,11 +80,14 @@ private class AdMessagesHistoryContextImpl { case peer(PeerId) case invite(Invite) case webPage(WebPage) + case botApp(PeerId, BotApp) init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - if let peer = try container.decodeIfPresent(Int64.self, forKey: .peer) { + if let botApp = try container.decodeIfPresent(BotApp.self, forKey: .botApp), let peer = try container.decodeIfPresent(Int64.self, forKey: .peer) { + self = .botApp(PeerId(peer), botApp) + } else if let peer = try container.decodeIfPresent(Int64.self, forKey: .peer) { self = .peer(PeerId(peer)) } else if let invite = try container.decodeIfPresent(Invite.self, forKey: .invite) { self = .invite(invite) @@ -103,6 +108,9 @@ private class AdMessagesHistoryContextImpl { try container.encode(invite, forKey: .invite) case let .webPage(webPage): try container.encode(webPage, forKey: .webPage) + case let .botApp(peerId, botApp): + try container.encode(peerId.toInt64(), forKey: .peer) + try container.encode(botApp, forKey: .botApp) } } } @@ -116,6 +124,7 @@ private class AdMessagesHistoryContextImpl { public let target: Target public let messageId: MessageId? public let startParam: String? + public let buttonText: String? public let sponsorInfo: String? public let additionalInfo: String? @@ -129,6 +138,7 @@ private class AdMessagesHistoryContextImpl { target: Target, messageId: MessageId?, startParam: String?, + buttonText: String?, sponsorInfo: String?, additionalInfo: String? ) { @@ -141,6 +151,7 @@ private class AdMessagesHistoryContextImpl { self.target = target self.messageId = messageId self.startParam = startParam + self.buttonText = buttonText self.sponsorInfo = sponsorInfo self.additionalInfo = additionalInfo } @@ -169,6 +180,7 @@ private class AdMessagesHistoryContextImpl { self.target = try container.decode(Target.self, forKey: .target) self.messageId = try container.decodeIfPresent(MessageId.self, forKey: .messageId) self.startParam = try container.decodeIfPresent(String.self, forKey: .startParam) + self.buttonText = try container.decodeIfPresent(String.self, forKey: .buttonText) self.sponsorInfo = try container.decodeIfPresent(String.self, forKey: .sponsorInfo) self.additionalInfo = try container.decodeIfPresent(String.self, forKey: .additionalInfo) @@ -193,6 +205,7 @@ private class AdMessagesHistoryContextImpl { try container.encode(self.target, forKey: .target) try container.encodeIfPresent(self.messageId, forKey: .messageId) try container.encodeIfPresent(self.startParam, forKey: .startParam) + try container.encodeIfPresent(self.buttonText, forKey: .buttonText) try container.encodeIfPresent(self.sponsorInfo, forKey: .sponsorInfo) try container.encodeIfPresent(self.additionalInfo, forKey: .additionalInfo) @@ -228,6 +241,9 @@ private class AdMessagesHistoryContextImpl { if lhs.startParam != rhs.startParam { return false } + if lhs.buttonText != rhs.buttonText { + return false + } if lhs.sponsorInfo != rhs.sponsorInfo { return false } @@ -248,6 +264,8 @@ private class AdMessagesHistoryContextImpl { target = .join(title: invite.title, joinHash: invite.joinHash) case let .webPage(webPage): target = .webPage(title: webPage.title, url: webPage.url) + case let .botApp(peerId, botApp): + target = .botApp(peerId: peerId, app: botApp, startParam: self.startParam) } let mappedMessageType: AdMessageAttribute.MessageType switch self.messageType { @@ -256,7 +274,7 @@ private class AdMessagesHistoryContextImpl { case .recommended: mappedMessageType = .recommended } - attributes.append(AdMessageAttribute(opaqueId: self.opaqueId, messageType: mappedMessageType, displayAvatar: self.displayAvatar, target: target, sponsorInfo: self.sponsorInfo, additionalInfo: self.additionalInfo)) + attributes.append(AdMessageAttribute(opaqueId: self.opaqueId, messageType: mappedMessageType, displayAvatar: self.displayAvatar, target: target, buttonText: self.buttonText, sponsorInfo: self.sponsorInfo, additionalInfo: self.additionalInfo)) if !self.textEntities.isEmpty { let attribute = TextEntitiesMessageAttribute(entities: self.textEntities) attributes.append(attribute) @@ -270,7 +288,7 @@ private class AdMessagesHistoryContextImpl { let author: Peer switch self.target { - case let .peer(peerId): + case let .peer(peerId), let .botApp(peerId, _): if let peer = transaction.getPeer(peerId) { author = peer } else { @@ -295,7 +313,9 @@ private class AdMessagesHistoryContextImpl { usernames: [], storiesHidden: nil, nameColor: invite.nameColor, - backgroundEmojiId: nil + backgroundEmojiId: nil, + profileColor: nil, + profileBackgroundEmojiId: nil ) case let .webPage(webPage): author = TelegramChannel( @@ -316,7 +336,9 @@ private class AdMessagesHistoryContextImpl { usernames: [], storiesHidden: nil, nameColor: .blue, - backgroundEmojiId: nil + backgroundEmojiId: nil, + profileColor: nil, + profileBackgroundEmojiId: nil ) } @@ -523,7 +545,7 @@ private class AdMessagesHistoryContextImpl { for message in messages { switch message { - case let .sponsoredMessage(flags, randomId, fromId, chatInvite, chatInviteHash, channelPost, startParam, webPage, message, entities, sponsorInfo, additionalInfo): + case let .sponsoredMessage(flags, randomId, fromId, chatInvite, chatInviteHash, channelPost, startParam, webPage, botApp, message, entities, buttonText, sponsorInfo, additionalInfo): var parsedEntities: [MessageTextEntity] = [] if let entities = entities { parsedEntities = messageTextEntitiesFromApiEntities(entities) @@ -534,7 +556,11 @@ private class AdMessagesHistoryContextImpl { var target: CachedMessage.Target? if let fromId = fromId { - target = .peer(fromId.peerId) + if let botApp = botApp, let app = BotApp(apiBotApp: botApp) { + target = .botApp(fromId.peerId, app) + } else { + target = .peer(fromId.peerId) + } } else if let webPage = webPage { switch webPage { case let .sponsoredWebPage(_, url, siteName, photo): @@ -574,7 +600,10 @@ private class AdMessagesHistoryContextImpl { )) } } - } + } +// else if let botApp = app.flatMap({ BotApp(apiBotApp: $0) }) { +// target = .botApp(botApp) +// } var messageId: MessageId? if let fromId = fromId, let channelPost = channelPost { @@ -592,6 +621,7 @@ private class AdMessagesHistoryContextImpl { target: target, messageId: messageId, startParam: startParam, + buttonText: buttonText, sponsorInfo: sponsorInfo, additionalInfo: additionalInfo )) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift index d37f5b10cf5..5fa7cd1cc19 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift @@ -149,7 +149,12 @@ func _internal_togglePeerUnreadMarkInteractively(transaction: Transaction, netwo return } - if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) { + var displayAsRegularChat: Bool = false + if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData { + displayAsRegularChat = cachedData.viewForumAsMessages.knownValue ?? false + } + + if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum), !displayAsRegularChat { for item in transaction.getMessageHistoryThreadIndex(peerId: peerId, limit: 20) { guard var data = transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: item.threadId)?.data.get(MessageHistoryThreadData.self) else { continue diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift index da7da6772d0..2649a4b3aa9 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift @@ -666,19 +666,8 @@ public final class BotApp: Equatable, Codable { self.shortName = try container.decode(String.self, forKey: .shortName) self.title = try container.decode(String.self, forKey: .title) self.description = try container.decode(String.self, forKey: .description) - - if let data = try container.decodeIfPresent(AdaptedPostboxDecoder.RawObjectData.self, forKey: .photo) { - self.photo = TelegramMediaImage(decoder: PostboxDecoder(buffer: MemoryBuffer(data: data.data))) - } else { - self.photo = nil - } - - if let data = try container.decodeIfPresent(AdaptedPostboxDecoder.RawObjectData.self, forKey: .document) { - self.document = TelegramMediaFile(decoder: PostboxDecoder(buffer: MemoryBuffer(data: data.data))) - } else { - self.document = nil - } - + self.photo = try container.decodeIfPresent(TelegramMediaImage.self, forKey: .photo) + self.document = try container.decodeIfPresent(TelegramMediaFile.self, forKey: .document) self.hash = try container.decode(Int64.self, forKey: .hash) self.flags = Flags(rawValue: try container.decode(Int32.self, forKey: .flags)) } @@ -767,12 +756,23 @@ func _internal_getBotApp(account: Account, reference: BotAppReference) -> Signal appFlags.insert(.hasSettings) } return .single(BotApp(id: id, accessHash: accessHash, shortName: shortName, title: title, description: description, photo: telegramMediaImageFromApiPhoto(photo), document: document.flatMap(telegramMediaFileFromApiDocument), hash: hash, flags: appFlags)) - case .botAppNotModified: - return .complete() - } + case .botAppNotModified: + return .complete() + } } } } |> castError(GetBotAppError.self) |> switchToLatest } + +extension BotApp { + convenience init?(apiBotApp: Api.BotApp) { + switch apiBotApp { + case let .botApp(_, id, accessHash, shortName, title, description, photo, document, hash): + self.init(id: id, accessHash: accessHash, shortName: shortName, title: title, description: description, photo: telegramMediaImageFromApiPhoto(photo), document: document.flatMap(telegramMediaFileFromApiDocument), hash: hash, flags: []) + case .botAppNotModified: + return nil + } + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift index ba6a04cbfee..7d2b62e30ed 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift @@ -83,7 +83,7 @@ private func keepWebViewSignal(network: Network, stateManager: AccountStateManag if threadId != nil { replyFlags |= 1 << 0 } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyToMessageId.id, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyToMessageId.id, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) } let signal: Signal = network.request(Api.functions.messages.prolongWebView(flags: flags, peer: peer, bot: bot, queryId: queryId, replyTo: replyTo, sendAs: sendAs)) |> mapError { _ -> KeepWebViewError in @@ -157,7 +157,7 @@ func _internal_requestWebView(postbox: Postbox, network: Network, stateManager: if threadId != nil { replyFlags |= 1 << 0 } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyToMessageId.id, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyToMessageId.id, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) } return network.request(Api.functions.messages.requestWebView(flags: flags, peer: inputPeer, bot: inputBot, url: url, startParam: payload, themeParams: serializedThemeParams, platform: botWebViewPlatform, replyTo: replyTo, sendAs: nil)) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift index f5f8dab3748..5a4955128cd 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift @@ -46,10 +46,9 @@ func _internal_clearCloudDraftsInteractively(postbox: Postbox, network: Network, if let topMsgId = topMsgId { flags |= (1 << 0) - //inputReplyToMessage#73ec805 flags:# reply_to_msg_id:int top_msg_id:flags.0?int reply_to_peer_id:flags.1?InputPeer quote_text:flags.2?string quote_entities:flags.3?Vector = InputReplyTo; var innerFlags: Int32 = 0 innerFlags |= 1 << 0 - replyTo = .inputReplyToMessage(flags: innerFlags, replyToMsgId: 0, topMsgId: topMsgId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil) + replyTo = .inputReplyToMessage(flags: innerFlags, replyToMsgId: 0, topMsgId: topMsgId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) } signals.append(network.request(Api.functions.messages.saveDraft(flags: flags, replyTo: replyTo, peer: inputPeer, message: "", entities: nil, media: nil)) |> `catch` { _ -> Signal in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/EngineStoryViewListContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/EngineStoryViewListContext.swift index c8fd5fee1cc..635c340d9e9 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/EngineStoryViewListContext.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/EngineStoryViewListContext.swift @@ -14,6 +14,7 @@ public final class EngineStoryViewListContext { } public enum SortMode { + case repostsFirst case reactionsFirst case recentFirst } @@ -176,6 +177,16 @@ public final class EngineStoryViewListContext { } } switch sortMode { + case .repostsFirst: + items.sort(by: { lhs, rhs in + if (lhs.reaction == nil) != (rhs.reaction == nil) { + return lhs.reaction != nil + } + if lhs.timestamp != rhs.timestamp { + return lhs.timestamp > rhs.timestamp + } + return lhs.peer.id < rhs.peer.id + }) case .reactionsFirst: items.sort(by: { lhs, rhs in if (lhs.reaction == nil) != (rhs.reaction == nil) { @@ -287,7 +298,7 @@ public final class EngineStoryViewListContext { switch sortMode { case .reactionsFirst: flags |= (1 << 2) - case .recentFirst: + case .recentFirst, .repostsFirst: break } if searchQuery != nil { @@ -376,7 +387,8 @@ public final class EngineStoryViewListContext { isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: item.myReaction + myReaction: item.myReaction, + forwardInfo: item.forwardInfo )) if let entry = CodableEntry(updatedItem) { transaction.setStory(id: StoryId(peerId: account.peerId, id: storyId), value: entry) @@ -413,7 +425,8 @@ public final class EngineStoryViewListContext { isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: item.myReaction + myReaction: item.myReaction, + forwardInfo: item.forwardInfo )) if let entry = CodableEntry(updatedItem) { currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/PendingStoryManager.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/PendingStoryManager.swift index 80534326c89..6c094379ee8 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/PendingStoryManager.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/PendingStoryManager.swift @@ -39,6 +39,40 @@ public extension Stories { } } + struct PendingForwardInfo: Codable, Equatable { + private enum CodingKeys: String, CodingKey { + case peerId = "peerId" + case storyId = "storyId" + case isModified = "isModified" + } + + public let peerId: EnginePeer.Id + public let storyId: Int32 + public let isModified: Bool + + public init(peerId: EnginePeer.Id, storyId: Int32, isModified: Bool) { + self.peerId = peerId + self.storyId = storyId + self.isModified = isModified + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.peerId = EnginePeer.Id(try container.decode(Int64.self, forKey: .peerId)) + self.storyId = try container.decode(Int32.self, forKey: .storyId) + self.isModified = try container.decodeIfPresent(Bool.self, forKey: .isModified) ?? false + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.peerId.toInt64(), forKey: .peerId) + try container.encode(self.storyId, forKey: .storyId) + try container.encode(self.isModified, forKey: .isModified) + } + } + final class PendingItem: Equatable, Codable { private enum CodingKeys: CodingKey { case target @@ -54,6 +88,7 @@ public extension Stories { case isForwardingDisabled case period case randomId + case forwardInfo } public let target: PendingTarget @@ -69,6 +104,7 @@ public extension Stories { public let isForwardingDisabled: Bool public let period: Int32 public let randomId: Int64 + public let forwardInfo: PendingForwardInfo? public init( target: PendingTarget, @@ -83,7 +119,8 @@ public extension Stories { privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int32, - randomId: Int64 + randomId: Int64, + forwardInfo: PendingForwardInfo? ) { self.target = target self.stableId = stableId @@ -98,6 +135,7 @@ public extension Stories { self.isForwardingDisabled = isForwardingDisabled self.period = period self.randomId = randomId + self.forwardInfo = forwardInfo } public init(from decoder: Decoder) throws { @@ -123,6 +161,8 @@ public extension Stories { self.isForwardingDisabled = try container.decodeIfPresent(Bool.self, forKey: .isForwardingDisabled) ?? false self.period = try container.decode(Int32.self, forKey: .period) self.randomId = try container.decode(Int64.self, forKey: .randomId) + + self.forwardInfo = try container.decodeIfPresent(PendingForwardInfo.self, forKey: .forwardInfo) } public func encode(to encoder: Encoder) throws { @@ -150,6 +190,7 @@ public extension Stories { try container.encode(self.isForwardingDisabled, forKey: .isForwardingDisabled) try container.encode(self.period, forKey: .period) try container.encode(self.randomId, forKey: .randomId) + try container.encodeIfPresent(self.forwardInfo, forKey: .forwardInfo) } public static func ==(lhs: PendingItem, rhs: PendingItem) -> Bool { @@ -186,6 +227,9 @@ public extension Stories { if lhs.randomId != rhs.randomId { return false } + if lhs.forwardInfo != rhs.forwardInfo { + return false + } return true } } @@ -359,8 +403,9 @@ final class PendingStoryManager { case let .peer(peerId): toPeerId = peerId } + let stableId = firstItem.stableId - pendingItemContext.disposable = (_internal_uploadStoryImpl(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, stateManager: self.stateManager, messageMediaPreuploadManager: self.messageMediaPreuploadManager, revalidationContext: self.revalidationContext, auxiliaryMethods: self.auxiliaryMethods, toPeerId: toPeerId, stableId: stableId, media: firstItem.media, mediaAreas: firstItem.mediaAreas, text: firstItem.text, entities: firstItem.entities, embeddedStickers: firstItem.embeddedStickers, pin: firstItem.pin, privacy: firstItem.privacy, isForwardingDisabled: firstItem.isForwardingDisabled, period: Int(firstItem.period), randomId: firstItem.randomId) + pendingItemContext.disposable = (_internal_uploadStoryImpl(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, stateManager: self.stateManager, messageMediaPreuploadManager: self.messageMediaPreuploadManager, revalidationContext: self.revalidationContext, auxiliaryMethods: self.auxiliaryMethods, toPeerId: toPeerId, stableId: stableId, media: firstItem.media, mediaAreas: firstItem.mediaAreas, text: firstItem.text, entities: firstItem.entities, embeddedStickers: firstItem.embeddedStickers, pin: firstItem.pin, privacy: firstItem.privacy, isForwardingDisabled: firstItem.isForwardingDisabled, period: Int(firstItem.period), randomId: firstItem.randomId, forwardInfo: firstItem.forwardInfo) |> deliverOn(self.queue)).start(next: { [weak self] event in guard let `self` = self else { return diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift index 63bb3ba7a40..ef8800d5da2 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift @@ -9,7 +9,7 @@ public enum SearchMessagesLocation: Equatable { case general(tags: MessageTags?, minDate: Int32?, maxDate: Int32?) case group(groupId: PeerGroupId, tags: MessageTags?, minDate: Int32?, maxDate: Int32?) case peer(peerId: PeerId, fromId: PeerId?, tags: MessageTags?, topMsgId: MessageId?, minDate: Int32?, maxDate: Int32?) - case publicForwards(messageId: MessageId, datacenterId: Int?) + case publicForwards(messageId: MessageId) case sentMedia(tags: MessageTags?) } @@ -354,30 +354,31 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation return .single((nil, nil)) } } - case let .publicForwards(messageId, datacenterId): - remoteSearchResult = account.postbox.transaction { transaction -> (Api.InputChannel?, Int32, MessageIndex?, Api.InputPeer) in + case let .publicForwards(messageId): + remoteSearchResult = account.postbox.transaction { transaction -> (Api.InputChannel?, Int32, MessageIndex?, Api.InputPeer, Int32?) in let sourcePeer = transaction.getPeer(messageId.peerId) let inputChannel = sourcePeer.flatMap { apiInputChannel($0) } + let statsDatacenterId = (transaction.getPeerCachedData(peerId: messageId.peerId) as? CachedChannelData)?.statsDatacenterId var lowerBound: MessageIndex? if let state = state, let message = state.main.messages.last { lowerBound = message.index } if let lowerBound = lowerBound, let peer = transaction.getPeer(lowerBound.id.peerId), let inputPeer = apiInputPeer(peer) { - return (inputChannel, state?.main.nextRate ?? 0, lowerBound, inputPeer) + return (inputChannel, state?.main.nextRate ?? 0, lowerBound, inputPeer, statsDatacenterId) } else { - return (inputChannel, 0, lowerBound, .inputPeerEmpty) + return (inputChannel, 0, lowerBound, .inputPeerEmpty, statsDatacenterId) } } - |> mapToSignal { (inputChannel, nextRate, lowerBound, inputPeer) in + |> mapToSignal { (inputChannel, nextRate, lowerBound, inputPeer, statsDatacenterId) in guard let inputChannel = inputChannel else { return .complete() } let request = Api.functions.stats.getMessagePublicForwards(channel: inputChannel, msgId: messageId.id, offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit) let signal: Signal - if let datacenterId = datacenterId, account.network.datacenterId != datacenterId { - signal = account.network.download(datacenterId: datacenterId, isMedia: false, tag: nil) + if let statsDatacenterId = statsDatacenterId, account.network.datacenterId != statsDatacenterId { + signal = account.network.download(datacenterId: Int(statsDatacenterId), isMedia: false, tag: nil) |> castError(MTRpcError.self) |> mapToSignal { worker in return worker.request(request) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift index 52dec1effb8..b875cec9618 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift @@ -138,7 +138,7 @@ func _internal_peerSendAsAvailablePeers(accountPeerId: PeerId, network: Network, for chat in chats { if let groupOrChannel = parsedPeers.get(chat.peerId) { switch chat { - case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _): + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _): if let participantsCount = participantsCount { subscribers[groupOrChannel.id] = participantsCount } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift index 839ae2ee1f2..1543dfb179e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift @@ -6,11 +6,14 @@ import TelegramApi public enum EngineStoryInputMedia { case image(dimensions: PixelDimensions, data: Data, stickers: [TelegramMediaFile]) case video(dimensions: PixelDimensions, duration: Double, resource: TelegramMediaResource, firstFrameFile: TempBoxFile?, stickers: [TelegramMediaFile]) + case existing(media: Media) var embeddedStickers: [TelegramMediaFile] { switch self { case let .image(_, _, stickers), let .video(_, _, _, _, stickers): return stickers + case .existing: + return [] } } } @@ -36,6 +39,34 @@ public extension EngineStoryPrivacy { } } +public extension EngineStoryItem.ForwardInfo { + init?(_ forwardInfo: Stories.Item.ForwardInfo, transaction: Transaction) { + switch forwardInfo { + case let .known(peerId, storyId, isModified): + if let peer = transaction.getPeer(peerId) { + self = .known(peer: EnginePeer(peer), storyId: storyId, isModified: isModified) + } else { + return nil + } + case let .unknown(name, isModified): + self = .unknown(name: name, isModified: isModified) + } + } + + init?(_ forwardInfo: Stories.Item.ForwardInfo, peers: [PeerId: Peer]) { + switch forwardInfo { + case let .known(peerId, storyId, isModified): + if let peer = peers[peerId] { + self = .known(peer: EnginePeer(peer), storyId: storyId, isModified: isModified) + } else { + return nil + } + case let .unknown(name, isModified): + self = .unknown(name: name, isModified: isModified) + } + } +} + public enum Stories { public final class Item: Codable, Equatable { public struct Views: Codable, Equatable { @@ -162,6 +193,52 @@ public enum Stories { } } + public enum ForwardInfo: Codable, Equatable { + public enum DecodingError: Error { + case generic + } + + private enum CodingKeys: CodingKey { + case discriminator + case authorPeerId + case storyId + case authorName + case isModified + } + + case known(peerId: EnginePeer.Id, storyId: Int32, isModified: Bool) + case unknown(name: String, isModified: Bool) + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + switch try container.decode(Int32.self, forKey: .discriminator) { + case 0: + self = .known(peerId: EnginePeer.Id(try container.decode(Int64.self, forKey: .authorPeerId)), storyId: try container.decode(Int32.self, forKey: .storyId), isModified: try container.decodeIfPresent(Bool.self, forKey: .isModified) ?? false) + case 1: + self = .unknown(name: try container.decode(String.self, forKey: .authorName), isModified: try container.decodeIfPresent(Bool.self, forKey: .isModified) ?? false) + default: + throw DecodingError.generic + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + switch self { + case let .known(peerId, storyId, isModified): + try container.encode(0 as Int32, forKey: .discriminator) + try container.encode(peerId.toInt64(), forKey: .authorPeerId) + try container.encode(storyId, forKey: .storyId) + try container.encode(isModified, forKey: .isModified) + case let .unknown(name, isModified): + try container.encode(1 as Int32, forKey: .discriminator) + try container.encode(name, forKey: .authorName) + try container.encode(isModified, forKey: .isModified) + } + } + } + private enum CodingKeys: String, CodingKey { case id case timestamp @@ -182,6 +259,7 @@ public enum Stories { case isEdited case isMy case myReaction + case forwardInfo } public let id: Int32 @@ -203,6 +281,7 @@ public enum Stories { public let isEdited: Bool public let isMy: Bool public let myReaction: MessageReaction.Reaction? + public let forwardInfo: ForwardInfo? public init( id: Int32, @@ -223,7 +302,8 @@ public enum Stories { isForwardingDisabled: Bool, isEdited: Bool, isMy: Bool, - myReaction: MessageReaction.Reaction? + myReaction: MessageReaction.Reaction?, + forwardInfo: ForwardInfo? ) { self.id = id self.timestamp = timestamp @@ -244,6 +324,7 @@ public enum Stories { self.isEdited = isEdited self.isMy = isMy self.myReaction = myReaction + self.forwardInfo = forwardInfo } public init(from decoder: Decoder) throws { @@ -274,6 +355,7 @@ public enum Stories { self.isEdited = try container.decodeIfPresent(Bool.self, forKey: .isEdited) ?? false self.isMy = try container.decodeIfPresent(Bool.self, forKey: .isMy) ?? false self.myReaction = try container.decodeIfPresent(MessageReaction.Reaction.self, forKey: .myReaction) + self.forwardInfo = try container.decodeIfPresent(ForwardInfo.self, forKey: .forwardInfo) } public func encode(to encoder: Encoder) throws { @@ -305,6 +387,7 @@ public enum Stories { try container.encode(self.isEdited, forKey: .isEdited) try container.encode(self.isMy, forKey: .isMy) try container.encodeIfPresent(self.myReaction, forKey: .myReaction) + try container.encodeIfPresent(self.forwardInfo, forKey: .forwardInfo) } public static func ==(lhs: Item, rhs: Item) -> Bool { @@ -369,7 +452,9 @@ public enum Stories { if lhs.myReaction != rhs.myReaction { return false } - + if lhs.forwardInfo != rhs.forwardInfo { + return false + } return true } } @@ -752,10 +837,12 @@ private func prepareUploadStoryContent(account: Account, media: EngineStoryInput ) return fileMedia + case let .existing(media): + return media } } -private func uploadedStoryContent(postbox: Postbox, network: Network, media: Media, embeddedStickers: [TelegramMediaFile], accountPeerId: PeerId, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, auxiliaryMethods: AccountAuxiliaryMethods, passFetchProgress: Bool) -> (signal: Signal, media: Media) { +private func uploadedStoryContent(postbox: Postbox, network: Network, media: Media, mediaReference: AnyMediaReference?, embeddedStickers: [TelegramMediaFile], accountPeerId: PeerId, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, auxiliaryMethods: AccountAuxiliaryMethods, passFetchProgress: Bool) -> (signal: Signal, media: Media) { let originalMedia: Media = media let contentToUpload: MessageContentToUpload @@ -780,7 +867,8 @@ private func uploadedStoryContent(postbox: Postbox, network: Network, media: Med messageId: nil, attributes: attributes, text: "", - media: [media] + media: [media], + mediaReference: mediaReference ) let contentSignal: Signal @@ -845,7 +933,7 @@ private func apiInputPrivacyRules(privacy: EngineStoryPrivacy, transaction: Tran return privacyRules } -func _internal_uploadStory(account: Account, target: Stories.PendingTarget, media: EngineStoryInputMedia, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int, randomId: Int64) -> Signal { +func _internal_uploadStory(account: Account, target: Stories.PendingTarget, media: EngineStoryInputMedia, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int, randomId: Int64, forwardInfo: Stories.PendingForwardInfo?) -> Signal { let inputMedia = prepareUploadStoryContent(account: account, media: media) return (account.postbox.transaction { transaction in @@ -872,7 +960,8 @@ func _internal_uploadStory(account: Account, target: Stories.PendingTarget, medi privacy: privacy, isForwardingDisabled: isForwardingDisabled, period: Int32(period), - randomId: randomId + randomId: randomId, + forwardInfo: forwardInfo )) transaction.setLocalStoryState(state: CodableEntry(currentState)) return stableId @@ -917,17 +1006,50 @@ private func _internal_putPendingStoryIdMapping(peerId: PeerId, stableId: Int32, } } -func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId: PeerId, stateManager: AccountStateManager, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, auxiliaryMethods: AccountAuxiliaryMethods, toPeerId: PeerId, stableId: Int32, media: Media, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], embeddedStickers: [TelegramMediaFile], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int, randomId: Int64) -> Signal { - return postbox.transaction { transaction -> Api.InputPeer? in - return transaction.getPeer(toPeerId).flatMap(apiInputPeer) +func _internal_uploadStoryImpl( + postbox: Postbox, + network: Network, + accountPeerId: PeerId, + stateManager: AccountStateManager, + messageMediaPreuploadManager: MessageMediaPreuploadManager, + revalidationContext: MediaReferenceRevalidationContext, + auxiliaryMethods: AccountAuxiliaryMethods, + toPeerId: PeerId, + stableId: Int32, + media: Media, + mediaAreas: [MediaArea], + text: String, + entities: [MessageTextEntity], + embeddedStickers: [TelegramMediaFile], + pin: Bool, + privacy: EngineStoryPrivacy, + isForwardingDisabled: Bool, + period: Int, + randomId: Int64, + forwardInfo: Stories.PendingForwardInfo? +) -> Signal { + return postbox.transaction { transaction -> (Peer, Peer?)? in + if let peer = transaction.getPeer(toPeerId) { + if let forwardInfo = forwardInfo { + return (peer, transaction.getPeer(forwardInfo.peerId)) + } else { + return (peer, nil) + } + } + return nil } - |> mapToSignal { inputPeer -> Signal in - guard let inputPeer = inputPeer else { + |> mapToSignal { inputPeerAndForwardInfoPeer -> Signal in + guard let (inputPeer, forwardInfoPeer) = inputPeerAndForwardInfoPeer, let inputPeer = apiInputPeer(inputPeer) else { return .single(.completed(nil)) } + var mediaReference: AnyMediaReference? + if let forwardInfo = forwardInfo, let forwardInfoPeer = forwardInfoPeer.flatMap(PeerReference.init) { + mediaReference = .story(peer: forwardInfoPeer, id: forwardInfo.storyId, media: media) + } + let passFetchProgress = media is TelegramMediaFile - let (contentSignal, originalMedia) = uploadedStoryContent(postbox: postbox, network: network, media: media, embeddedStickers: embeddedStickers, accountPeerId: accountPeerId, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, auxiliaryMethods: auxiliaryMethods, passFetchProgress: passFetchProgress) + let (contentSignal, originalMedia) = uploadedStoryContent(postbox: postbox, network: network, media: media, mediaReference: mediaReference, embeddedStickers: embeddedStickers, accountPeerId: accountPeerId, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, auxiliaryMethods: auxiliaryMethods, passFetchProgress: passFetchProgress) return contentSignal |> mapToSignal { result -> Signal in switch result { @@ -975,6 +1097,17 @@ func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId flags |= 1 << 5 } + var fwdFromId: Api.InputPeer? + var fwdFromStory: Int32? + if let forwardInfo = forwardInfo, let inputPeer = transaction.getPeer(forwardInfo.peerId).flatMap({ apiInputPeer($0) }) { + flags |= 1 << 6 + if forwardInfo.isModified { + flags |= 1 << 7 + } + fwdFromId = inputPeer + fwdFromStory = forwardInfo.storyId + } + return network.request(Api.functions.stories.sendStory( flags: flags, peer: inputPeer, @@ -984,7 +1117,9 @@ func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId entities: apiEntities, privacyRules: privacyRules, randomId: randomId, - period: Int32(period) + period: Int32(period), + fwdFromId: fwdFromId, + fwdFromStory: fwdFromStory )) |> map(Optional.init) |> `catch` { _ -> Signal in @@ -1008,7 +1143,7 @@ func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId for update in updates.allUpdates { if case let .updateStory(_, story) = update { switch story { - case let .storyItem(_, idValue, _, _, _, _, media, _, _, _, _): + case let .storyItem(_, idValue, _, _, _, _, _, media, _, _, _, _): if let parsedStory = Stories.StoredItem(apiStoryItem: story, peerId: toPeerId, transaction: transaction) { var items = transaction.getStoryItems(peerId: toPeerId) var updatedItems: [Stories.Item] = [] @@ -1032,7 +1167,8 @@ func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: item.myReaction + myReaction: item.myReaction, + forwardInfo: item.forwardInfo ) if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) { items.append(StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)) @@ -1092,7 +1228,7 @@ func _internal_editStory(account: Account, peerId: PeerId, id: Int32, media: Eng if case .video = media { passFetchProgress = true } - (contentSignal, originalMedia) = uploadedStoryContent(postbox: account.postbox, network: account.network, media: prepareUploadStoryContent(account: account, media: media), embeddedStickers: media.embeddedStickers, accountPeerId: account.peerId, messageMediaPreuploadManager: account.messageMediaPreuploadManager, revalidationContext: account.mediaReferenceRevalidationContext, auxiliaryMethods: account.auxiliaryMethods, passFetchProgress: passFetchProgress) + (contentSignal, originalMedia) = uploadedStoryContent(postbox: account.postbox, network: account.network, media: prepareUploadStoryContent(account: account, media: media), mediaReference: nil, embeddedStickers: media.embeddedStickers, accountPeerId: account.peerId, messageMediaPreuploadManager: account.messageMediaPreuploadManager, revalidationContext: account.mediaReferenceRevalidationContext, auxiliaryMethods: account.auxiliaryMethods, passFetchProgress: passFetchProgress) } else { contentSignal = .single(nil) originalMedia = nil @@ -1169,7 +1305,7 @@ func _internal_editStory(account: Account, peerId: PeerId, id: Int32, media: Eng for update in updates.allUpdates { if case let .updateStory(_, story) = update { switch story { - case let .storyItem(_, _, _, _, _, _, media, _, _, _, _): + case let .storyItem(_, _, _, _, _, _, _, media, _, _, _, _): let (parsedMedia, _, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, account.peerId) if let parsedMedia = parsedMedia, let originalMedia = originalMedia { applyMediaResourceChanges(from: originalMedia, to: parsedMedia, postbox: account.postbox, force: false) @@ -1212,7 +1348,8 @@ func _internal_editStoryPrivacy(account: Account, id: Int32, privacy: EngineStor isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: item.myReaction + myReaction: item.myReaction, + forwardInfo: item.forwardInfo ) if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) { transaction.setStory(id: storyId, value: entry) @@ -1241,7 +1378,8 @@ func _internal_editStoryPrivacy(account: Account, id: Int32, privacy: EngineStor isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: item.myReaction + myReaction: item.myReaction, + forwardInfo: item.forwardInfo ) if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) { items[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends) @@ -1433,7 +1571,8 @@ func _internal_updateStoriesArePinned(account: Account, peerId: PeerId, ids: [In isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: item.myReaction + myReaction: item.myReaction, + forwardInfo: item.forwardInfo ) if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) { items[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends) @@ -1461,7 +1600,8 @@ func _internal_updateStoriesArePinned(account: Account, peerId: PeerId, ids: [In isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: item.myReaction + myReaction: item.myReaction, + forwardInfo: item.forwardInfo ) updatedItems.append(updatedItem) } @@ -1493,7 +1633,7 @@ func _internal_updateStoriesArePinned(account: Account, peerId: PeerId, ids: [In extension Api.StoryItem { var id: Int32 { switch self { - case let .storyItem(_, id, _, _, _, _, _, _, _, _, _): + case let .storyItem(_, id, _, _, _, _, _, _, _, _, _, _): return id case let .storyItemDeleted(id): return id @@ -1536,10 +1676,27 @@ extension Stories.Item.Views { } } +extension Stories.Item.ForwardInfo { + init?(apiForwardInfo: Api.StoryFwdHeader) { + switch apiForwardInfo { + case let .storyFwdHeader(flags, from, fromName, storyId): + let isModified = (flags & (1 << 3)) != 0 + if let from = from, let storyId = storyId { + self = .known(peerId: from.peerId, storyId: storyId, isModified: isModified) + return + } else if let fromName = fromName { + self = .unknown(name: fromName, isModified: isModified) + return + } + } + return nil + } +} + extension Stories.StoredItem { init?(apiStoryItem: Api.StoryItem, existingItem: Stories.Item? = nil, peerId: PeerId, transaction: Transaction) { switch apiStoryItem { - case let .storyItem(flags, id, date, expireDate, caption, entities, media, mediaAreas, privacy, views, sentReaction): + case let .storyItem(flags, id, date, forwardFrom, expireDate, caption, entities, media, mediaAreas, privacy, views, sentReaction): let (parsedMedia, _, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, peerId) if let parsedMedia = parsedMedia { var parsedPrivacy: Stories.Item.Privacy? @@ -1610,6 +1767,13 @@ extension Stories.StoredItem { mergedIsMy = (flags & (1 << 16)) != 0 } + var mergedForwardInfo: Stories.Item.ForwardInfo? + if isMin, let existingItem = existingItem { + mergedForwardInfo = existingItem.forwardInfo + } else { + mergedForwardInfo = forwardFrom.flatMap(Stories.Item.ForwardInfo.init(apiForwardInfo:)) + } + let item = Stories.Item( id: id, timestamp: date, @@ -1629,7 +1793,8 @@ extension Stories.StoredItem { isForwardingDisabled: isForwardingDisabled, isEdited: isEdited, isMy: mergedIsMy, - myReaction: mergedMyReaction + myReaction: mergedMyReaction, + forwardInfo: mergedForwardInfo ) self = .item(item) } else { @@ -1644,6 +1809,77 @@ extension Stories.StoredItem { } } +func _internal_getStoryById(accountPeerId: PeerId, postbox: Postbox, network: Network, peerId: EnginePeer.Id, id: Int32) -> Signal { + let storyId = StoryId(peerId: peerId, id: id) + return postbox.transaction { transaction -> Peer? in + return transaction.getPeer(peerId) + } + |> mapToSignal { peer -> Signal in + guard let inputPeer = peer.flatMap(apiInputPeer) else { + return .single(nil) + } + return network.request(Api.functions.stories.getStoriesByID(peer: inputPeer, id: [id])) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal in + guard let result = result else { + return .single(nil) + } + return postbox.transaction { transaction -> EngineStoryItem? in + switch result { + case let .stories(_, stories, chats, users): + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(transaction: transaction, chats: chats, users: users)) + + if let storyItem = stories.first.flatMap({ Stories.StoredItem(apiStoryItem: $0, peerId: peerId, transaction: transaction) }) { + if let entry = CodableEntry(storyItem) { + transaction.setStory(id: storyId, value: entry) + } + if case let .item(item) = storyItem, let media = item.media { + return EngineStoryItem( + id: item.id, + timestamp: item.timestamp, + expirationTimestamp: item.expirationTimestamp, + media: EngineMedia(media), + mediaAreas: item.mediaAreas, + text: item.text, + entities: item.entities, + views: item.views.flatMap { views in + return EngineStoryItem.Views( + seenCount: views.seenCount, + reactedCount: views.reactedCount, + forwardCount: views.forwardCount, + seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in + return transaction.getPeer(id).flatMap(EnginePeer.init) + }, + reactions: views.reactions, + hasList: views.hasList + ) + }, + privacy: item.privacy.flatMap(EngineStoryPrivacy.init), + isPinned: item.isPinned, + isExpired: item.isExpired, + isPublic: item.isPublic, + isPending: false, + isCloseFriends: item.isCloseFriends, + isContacts: item.isContacts, + isSelectedContacts: item.isSelectedContacts, + isForwardingDisabled: item.isForwardingDisabled, + isEdited: item.isEdited, + isMy: item.isMy, + myReaction: item.myReaction, + forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) } + ) + } + } + return nil + } + } + } + } +} + func _internal_getStoriesById(accountPeerId: PeerId, postbox: Postbox, network: Network, peer: PeerReference, ids: [Int32]) -> Signal<[Stories.StoredItem], NoError> { let inputPeer = peer.inputPeer @@ -2101,7 +2337,8 @@ func _internal_setStoryReaction(account: Account, peerId: EnginePeer.Id, id: Int isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: reaction + myReaction: reaction, + forwardInfo: item.forwardInfo )) updatedItemValue = updatedItem if let entry = CodableEntry(updatedItem) { @@ -2132,7 +2369,8 @@ func _internal_setStoryReaction(account: Account, peerId: EnginePeer.Id, id: Int isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: reaction + myReaction: reaction, + forwardInfo: item.forwardInfo )) updatedItemValue = updatedItem if let entry = CodableEntry(updatedItem) { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift index 2eff63d186e..07ed8462882 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift @@ -51,6 +51,11 @@ public final class EngineStoryItem: Equatable { } } + public enum ForwardInfo: Equatable { + case known(peer: EnginePeer, storyId: Int32, isModified: Bool) + case unknown(name: String, isModified: Bool) + } + public let id: Int32 public let timestamp: Int32 public let expirationTimestamp: Int32 @@ -71,8 +76,9 @@ public final class EngineStoryItem: Equatable { public let isEdited: Bool public let isMy: Bool public let myReaction: MessageReaction.Reaction? + public let forwardInfo: ForwardInfo? - public init(id: Int32, timestamp: Int32, expirationTimestamp: Int32, media: EngineMedia, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], views: Views?, privacy: EngineStoryPrivacy?, isPinned: Bool, isExpired: Bool, isPublic: Bool, isPending: Bool, isCloseFriends: Bool, isContacts: Bool, isSelectedContacts: Bool, isForwardingDisabled: Bool, isEdited: Bool, isMy: Bool, myReaction: MessageReaction.Reaction?) { + public init(id: Int32, timestamp: Int32, expirationTimestamp: Int32, media: EngineMedia, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], views: Views?, privacy: EngineStoryPrivacy?, isPinned: Bool, isExpired: Bool, isPublic: Bool, isPending: Bool, isCloseFriends: Bool, isContacts: Bool, isSelectedContacts: Bool, isForwardingDisabled: Bool, isEdited: Bool, isMy: Bool, myReaction: MessageReaction.Reaction?, forwardInfo: ForwardInfo?) { self.id = id self.timestamp = timestamp self.expirationTimestamp = expirationTimestamp @@ -93,6 +99,7 @@ public final class EngineStoryItem: Equatable { self.isEdited = isEdited self.isMy = isMy self.myReaction = myReaction + self.forwardInfo = forwardInfo } public static func ==(lhs: EngineStoryItem, rhs: EngineStoryItem) -> Bool { @@ -156,11 +163,25 @@ public final class EngineStoryItem: Equatable { if lhs.myReaction != rhs.myReaction { return false } + if lhs.forwardInfo != rhs.forwardInfo { + return false + } return true } } -extension EngineStoryItem { +extension EngineStoryItem.ForwardInfo { + var storedForwardInfo: Stories.Item.ForwardInfo { + switch self { + case let .known(peer, storyId, isModified): + return .known(peerId: peer.id, storyId: storyId, isModified: isModified) + case let .unknown(name, isModified): + return .unknown(name: name, isModified: isModified) + } + } +} + +public extension EngineStoryItem { func asStoryItem() -> Stories.Item { return Stories.Item( id: self.id, @@ -195,7 +216,8 @@ extension EngineStoryItem { isForwardingDisabled: self.isForwardingDisabled, isEdited: self.isEdited, isMy: self.isMy, - myReaction: self.myReaction + myReaction: self.myReaction, + forwardInfo: self.forwardInfo?.storedForwardInfo ) } } @@ -570,7 +592,8 @@ public final class PeerStoryListContext { isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: item.myReaction + myReaction: item.myReaction, + forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) } ) items.append(mappedItem) @@ -713,7 +736,8 @@ public final class PeerStoryListContext { isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: item.myReaction + myReaction: item.myReaction, + forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) } ) storyItems.append(mappedItem) } @@ -802,6 +826,11 @@ public final class PeerStoryListContext { } } } + if let forwardInfo = item.forwardInfo, case let .known(peerId, _, _) = forwardInfo { + if let peer = transaction.getPeer(peerId) { + peers[peer.id] = peer + } + } } } default: @@ -868,7 +897,8 @@ public final class PeerStoryListContext { isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: item.myReaction + myReaction: item.myReaction, + forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) } ) finalUpdatedState = updatedState } @@ -914,7 +944,8 @@ public final class PeerStoryListContext { isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: item.myReaction + myReaction: item.myReaction, + forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) } ) finalUpdatedState = updatedState } else { @@ -962,7 +993,8 @@ public final class PeerStoryListContext { isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: item.myReaction + myReaction: item.myReaction, + forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) } )) updatedState.items.sort(by: { lhs, rhs in return lhs.timestamp > rhs.timestamp @@ -1006,7 +1038,8 @@ public final class PeerStoryListContext { isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: item.myReaction + myReaction: item.myReaction, + forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) } )) updatedState.items.sort(by: { lhs, rhs in return lhs.timestamp > rhs.timestamp @@ -1174,7 +1207,8 @@ public final class PeerExpiringStoryListContext { isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: item.myReaction + myReaction: item.myReaction, + forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) } ) items.append(.item(mappedItem)) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index 141ebef0d45..cde56283f50 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -490,7 +490,7 @@ public extension TelegramEngine { public func transcribeAudio(messageId: MessageId) -> Signal { return _internal_transcribeAudio(postbox: self.account.postbox, network: self.account.network, messageId: messageId) } - + public func storeLocallyTranscribedAudio(messageId: MessageId, text: String, isFinal: Bool, error: AudioTranscriptionMessageAttribute.TranscriptionError?) -> Signal { return self.account.postbox.transaction { transaction -> Void in transaction.updateMessage(messageId, update: { currentMessage in @@ -1192,7 +1192,8 @@ public extension TelegramEngine { isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: item.myReaction + myReaction: item.myReaction, + forwardInfo: item.forwardInfo )) if let entry = CodableEntry(updatedItem) { currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends) @@ -1206,8 +1207,8 @@ public extension TelegramEngine { } } - public func uploadStory(target: Stories.PendingTarget, media: EngineStoryInputMedia, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int, randomId: Int64) -> Signal { - return _internal_uploadStory(account: self.account, target: target, media: media, mediaAreas: mediaAreas, text: text, entities: entities, pin: pin, privacy: privacy, isForwardingDisabled: isForwardingDisabled, period: period, randomId: randomId) + public func uploadStory(target: Stories.PendingTarget, media: EngineStoryInputMedia, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int, randomId: Int64, forwardInfo: Stories.PendingForwardInfo?) -> Signal { + return _internal_uploadStory(account: self.account, target: target, media: media, mediaAreas: mediaAreas, text: text, entities: entities, pin: pin, privacy: privacy, isForwardingDisabled: isForwardingDisabled, period: period, randomId: randomId, forwardInfo: forwardInfo) } public func allStoriesUploadEvents() -> Signal<(Int32, Int32), NoError> { @@ -1278,5 +1279,9 @@ public extension TelegramEngine { public func setStoryReaction(peerId: EnginePeer.Id, id: Int32, reaction: MessageReaction.Reaction?) -> Signal { return _internal_setStoryReaction(account: self.account, peerId: peerId, id: id, reaction: reaction) } + + public func getStory(peerId: EnginePeer.Id, id: Int32) -> Signal { + return _internal_getStoryById(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, peerId: peerId, id: id) + } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Transcription.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Transcription.swift new file mode 100644 index 00000000000..ec7911a7b45 --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Transcription.swift @@ -0,0 +1,174 @@ +import Foundation +import Postbox +import SwiftSignalKit +import TelegramApi +import MtProtoKit + +public enum EngineAudioTranscriptionResult { + case success + case error +} + +private enum InternalAudioTranscriptionResult { + case success(Api.messages.TranscribedAudio) + case error(AudioTranscriptionMessageAttribute.TranscriptionError) + case limitExceeded(Int32) +} + +func _internal_transcribeAudio(postbox: Postbox, network: Network, messageId: MessageId) -> Signal { + return postbox.transaction { transaction -> Api.InputPeer? in + return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer) + } + |> mapToSignal { inputPeer -> Signal in + guard let inputPeer = inputPeer else { + return .single(.error) + } + return network.request(Api.functions.messages.transcribeAudio(peer: inputPeer, msgId: messageId.id)) + |> map { result -> InternalAudioTranscriptionResult in + return .success(result) + } + |> `catch` { error -> Signal in + let mappedError: AudioTranscriptionMessageAttribute.TranscriptionError + if error.errorDescription.hasPrefix("FLOOD_WAIT_") { + if let range = error.errorDescription.range(of: "_", options: .backwards) { + if let value = Int32(error.errorDescription[range.upperBound...]) { + return .single(.limitExceeded(value)) + } + } + mappedError = .generic + } else if error.errorDescription == "MSG_VOICE_TOO_LONG" { + mappedError = .tooLong + } else { + mappedError = .generic + } + return .single(.error(mappedError)) + } + |> mapToSignal { result -> Signal in + return postbox.transaction { transaction -> EngineAudioTranscriptionResult in + let updatedAttribute: AudioTranscriptionMessageAttribute + switch result { + case let .success(transcribedAudio): + switch transcribedAudio { + case let .transcribedAudio(flags, transcriptionId, text, trialRemainingCount, trialUntilDate): + let isPending = (flags & (1 << 0)) != 0 + updatedAttribute = AudioTranscriptionMessageAttribute(id: transcriptionId, text: text, isPending: isPending, didRate: false, error: nil) + + _internal_updateAudioTranscriptionTrialState(transaction: transaction) { current in + var updated = current + if let trialRemainingCount = trialRemainingCount, trialRemainingCount > 0 { + updated = updated.withUpdatedRemainingCount(trialRemainingCount) + } else if let trialUntilDate = trialUntilDate { + updated = updated.withUpdatedCooldownUntilTime(trialUntilDate) + } else { + updated = updated.withUpdatedCooldownUntilTime(nil) + } + return updated + } + } + case let .error(error): + updatedAttribute = AudioTranscriptionMessageAttribute(id: 0, text: "", isPending: false, didRate: false, error: error) + case let .limitExceeded(timeout): + let cooldownTime = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) + timeout + _internal_updateAudioTranscriptionTrialState(transaction: transaction) { current in + var updated = current + updated = updated.withUpdatedCooldownUntilTime(cooldownTime) + return updated + } + return .error + } + + transaction.updateMessage(messageId, update: { currentMessage in + let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init) + var attributes = currentMessage.attributes.filter { !($0 is AudioTranscriptionMessageAttribute) } + + 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)) + }) + + if updatedAttribute.error == nil { + return .success + } else { + return .error + } + } + } + } +} + +func _internal_rateAudioTranscription(postbox: Postbox, network: Network, messageId: MessageId, id: Int64, isGood: Bool) -> Signal { + return postbox.transaction { transaction -> Api.InputPeer? in + transaction.updateMessage(messageId, 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 + for i in 0 ..< attributes.count { + if let attribute = attributes[i] as? AudioTranscriptionMessageAttribute { + attributes[i] = attribute.withDidRate() + } + } + 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 transaction.getPeer(messageId.peerId).flatMap(apiInputPeer) + } + |> mapToSignal { inputPeer -> Signal in + guard let inputPeer = inputPeer else { + return .complete() + } + return network.request(Api.functions.messages.rateTranscribedAudio(peer: inputPeer, msgId: messageId.id, transcriptionId: id, good: isGood ? .boolTrue : .boolFalse)) + |> `catch` { _ -> Signal in + return .single(.boolFalse) + } + |> ignoreValues + } +} + +public enum AudioTranscription { + public struct TrialState: Equatable, Codable { + public let cooldownUntilTime: Int32? + public let remainingCount: Int32 + + func withUpdatedCooldownUntilTime(_ time: Int32?) -> AudioTranscription.TrialState { + return AudioTranscription.TrialState(cooldownUntilTime: time, remainingCount: time != nil ? 0 : max(1, self.remainingCount)) + } + + func withUpdatedRemainingCount(_ remainingCount: Int32) -> AudioTranscription.TrialState { + return AudioTranscription.TrialState(remainingCount: remainingCount) + } + + public init(cooldownUntilTime: Int32? = nil, remainingCount: Int32) { + self.cooldownUntilTime = cooldownUntilTime + self.remainingCount = remainingCount + } + + public static var defaultValue: AudioTranscription.TrialState { + return AudioTranscription.TrialState( + cooldownUntilTime: nil, + remainingCount: 1 + ) + } + } +} + +func _internal_updateAudioTranscriptionTrialState(transaction: Transaction, _ f: (AudioTranscription.TrialState) -> AudioTranscription.TrialState) { + let current = transaction.getPreferencesEntry(key: PreferencesKeys.audioTranscriptionTrialState)?.get(AudioTranscription.TrialState.self) ?? .defaultValue + transaction.setPreferencesEntry(key: PreferencesKeys.audioTranscriptionTrialState, value: PreferencesEntry(f(current))) +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift index f1ffae69530..562d549a7c4 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift @@ -155,108 +155,3 @@ func _internal_togglePeerMessagesTranslationHidden(account: Account, peerId: Eng |> ignoreValues } } - -public enum EngineAudioTranscriptionResult { - case success - case error -} - -func _internal_transcribeAudio(postbox: Postbox, network: Network, messageId: MessageId) -> Signal { - return postbox.transaction { transaction -> Api.InputPeer? in - return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer) - } - |> mapToSignal { inputPeer -> Signal in - guard let inputPeer = inputPeer else { - return .single(.error) - } - return network.request(Api.functions.messages.transcribeAudio(peer: inputPeer, msgId: messageId.id)) - |> map { result -> Result in - return .success(result) - } - |> `catch` { error -> Signal, NoError> in - let mappedError: AudioTranscriptionMessageAttribute.TranscriptionError - if error.errorDescription == "MSG_VOICE_TOO_LONG" { - mappedError = .tooLong - } else { - mappedError = .generic - } - return .single(.failure(mappedError)) - } - |> mapToSignal { result -> Signal in - return postbox.transaction { transaction -> EngineAudioTranscriptionResult in - let updatedAttribute: AudioTranscriptionMessageAttribute - switch result { - case let .success(transcribedAudio): - switch transcribedAudio { - case let .transcribedAudio(flags, transcriptionId, text): - let isPending = (flags & (1 << 0)) != 0 - - updatedAttribute = AudioTranscriptionMessageAttribute(id: transcriptionId, text: text, isPending: isPending, didRate: false, error: nil) - } - case let .failure(error): - updatedAttribute = AudioTranscriptionMessageAttribute(id: 0, text: "", isPending: false, didRate: false, error: error) - } - - transaction.updateMessage(messageId, update: { currentMessage in - let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init) - var attributes = currentMessage.attributes.filter { !($0 is AudioTranscriptionMessageAttribute) } - - 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)) - }) - - if updatedAttribute.error == nil { - return .success - } else { - return .error - } - } - } - } -} - -func _internal_rateAudioTranscription(postbox: Postbox, network: Network, messageId: MessageId, id: Int64, isGood: Bool) -> Signal { - return postbox.transaction { transaction -> Api.InputPeer? in - transaction.updateMessage(messageId, 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 - for i in 0 ..< attributes.count { - if let attribute = attributes[i] as? AudioTranscriptionMessageAttribute { - attributes[i] = attribute.withDidRate() - } - } - 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 transaction.getPeer(messageId.peerId).flatMap(apiInputPeer) - } - |> mapToSignal { inputPeer -> Signal in - guard let inputPeer = inputPeer else { - return .complete() - } - return network.request(Api.functions.messages.rateTranscribedAudio(peer: inputPeer, msgId: messageId.id, transcriptionId: id, good: isGood ? .boolTrue : .boolFalse)) - |> `catch` { _ -> Signal in - return .single(.boolFalse) - } - |> ignoreValues - } -} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/GiftCodes.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/GiftCodes.swift index 14767968275..784a06086eb 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/GiftCodes.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/GiftCodes.swift @@ -122,10 +122,10 @@ func _internal_getPremiumGiveawayInfo(account: Account, peerId: EnginePeer.Id, m } case let .giveawayInfoResults(flags, startDate, giftCodeSlug, finishDate, winnersCount, activatedCount): let status: PremiumGiveawayInfo.ResultStatus - if let giftCodeSlug = giftCodeSlug { - status = .won(slug: giftCodeSlug) - } else if (flags & (1 << 1)) != 0 { + if (flags & (1 << 1)) != 0 { status = .refunded + } else if let giftCodeSlug = giftCodeSlug { + status = .won(slug: giftCodeSlug) } else { status = .notWon } @@ -182,23 +182,27 @@ func _internal_checkPremiumGiftCode(account: Account, slug: String) -> Signal Signal { +public enum ApplyPremiumGiftCodeError { + case generic +} + +func _internal_applyPremiumGiftCode(account: Account, slug: String) -> Signal { return account.network.request(Api.functions.payments.applyGiftCode(slug: slug)) - |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) + |> mapError { _ -> ApplyPremiumGiftCodeError in + return .generic } - |> mapToSignal { updates -> Signal in - if let updates = updates { - account.stateManager.addUpdates(updates) - } - + |> mapToSignal { updates -> Signal in + account.stateManager.addUpdates(updates) return .complete() } } -func _internal_launchPrepaidGiveaway(account: Account, peerId: EnginePeer.Id, id: Int64, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, randomId: Int64, untilDate: Int32) -> Signal { - return account.postbox.transaction { transaction -> Signal in +public enum LaunchPrepaidGiveawayError { + case generic +} + +func _internal_launchPrepaidGiveaway(account: Account, peerId: EnginePeer.Id, id: Int64, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, randomId: Int64, untilDate: Int32) -> Signal { + return account.postbox.transaction { transaction -> Signal in var flags: Int32 = 0 if onlyNewSubscribers { flags |= (1 << 0) @@ -227,17 +231,15 @@ func _internal_launchPrepaidGiveaway(account: Account, peerId: EnginePeer.Id, id return .complete() } return account.network.request(Api.functions.payments.launchPrepaidGiveaway(peer: inputPeer, giveawayId: id, purpose: .inputStorePaymentPremiumGiveaway(flags: flags, boostPeer: inputPeer, additionalPeers: additionalPeers, countriesIso2: countries, randomId: randomId, untilDate: untilDate, currency: "", amount: 0))) - |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) + |> mapError { _ -> LaunchPrepaidGiveawayError in + return .generic } - |> mapToSignal { updates -> Signal in - if let updates = updates { - account.stateManager.addUpdates(updates) - } + |> mapToSignal { updates -> Signal in + account.stateManager.addUpdates(updates) return .complete() } } + |> castError(LaunchPrepaidGiveawayError.self) |> switchToLatest } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift index 39b6e913899..d275bc2f265 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift @@ -50,7 +50,7 @@ public extension TelegramEngine { return _internal_checkPremiumGiftCode(account: self.account, slug: slug) } - public func applyPremiumGiftCode(slug: String) -> Signal { + public func applyPremiumGiftCode(slug: String) -> Signal { return _internal_applyPremiumGiftCode(account: self.account, slug: slug) } @@ -62,7 +62,7 @@ public extension TelegramEngine { return _internal_getPremiumGiveawayInfo(account: self.account, peerId: peerId, messageId: messageId) } - public func launchPrepaidGiveaway(peerId: EnginePeer.Id, id: Int64, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, randomId: Int64, untilDate: Int32) -> Signal { + public func launchPrepaidGiveaway(peerId: EnginePeer.Id, id: Int64, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, randomId: Int64, untilDate: Int32) -> Signal { return _internal_launchPrepaidGiveaway(account: self.account, peerId: peerId, id: id, additionalPeerIds: additionalPeerIds, countries: countries, onlyNewSubscribers: onlyNewSubscribers, randomId: randomId, untilDate: untilDate) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddressNames.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddressNames.swift index 6edfce7e370..be5115b6dc3 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddressNames.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddressNames.swift @@ -595,7 +595,7 @@ func _internal_channelsForStories(account: Account) -> Signal<[Peer], NoError> { if let peer = transaction.getPeer(chat.peerId) { peers.append(peer) - if case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _) = chat, let participantsCount = participantsCount { + if case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _) = chat, let participantsCount = participantsCount { transaction.updatePeerCachedData(peerIds: Set([peer.id]), update: { _, current in var current = current as? CachedChannelData ?? CachedChannelData() var participantsSummary = current.participantsSummary diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelRecommendation.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelRecommendation.swift new file mode 100644 index 00000000000..2d726627b9a --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelRecommendation.swift @@ -0,0 +1,145 @@ +import Foundation +import Postbox +import SwiftSignalKit +import TelegramApi +import MtProtoKit + +final class CachedRecommendedChannels: Codable { + public let peerIds: [EnginePeer.Id] + public let count: Int32 + public let isHidden: Bool + + public init(peerIds: [EnginePeer.Id], count: Int32, isHidden: Bool) { + self.peerIds = peerIds + self.count = count + self.isHidden = isHidden + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + + self.peerIds = try container.decode([Int64].self, forKey: "l").map(EnginePeer.Id.init) + self.count = try container.decodeIfPresent(Int32.self, forKey: "c") ?? 0 + self.isHidden = try container.decode(Bool.self, forKey: "h") + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + try container.encode(self.peerIds.map { $0.toInt64() }, forKey: "l") + try container.encode(self.count, forKey: "c") + try container.encode(self.isHidden, forKey: "h") + } +} + +private func entryId(peerId: EnginePeer.Id) -> ItemCacheEntryId { + let cacheKey = ValueBoxKey(length: 8) + cacheKey.setInt64(0, value: peerId.toInt64()) + return ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.recommendedChannels, key: cacheKey) +} + +func _internal_requestRecommendedChannels(account: Account, peerId: EnginePeer.Id, forceUpdate: Bool) -> Signal { + return account.postbox.transaction { transaction -> Peer? in + guard let channel = transaction.getPeer(peerId) as? TelegramChannel, case .broadcast = channel.info else { + return nil + } + if let entry = transaction.retrieveItemCacheEntry(id: entryId(peerId: peerId))?.get(CachedRecommendedChannels.self), !entry.peerIds.isEmpty && !forceUpdate { + return nil + } else { + return channel + } + } + |> mapToSignal { channel in + guard let inputChannel = channel.flatMap(apiInputChannel) else { + return .complete() + } + return account.network.request(Api.functions.channels.getChannelRecommendations(channel: inputChannel)) + |> retryRequest + |> mapToSignal { result -> Signal in + return account.postbox.transaction { transaction -> [EnginePeer] in + let chats: [Api.Chat] + let parsedPeers: AccumulatedPeers + var count: Int32 + switch result { + case let .chats(apiChats): + chats = apiChats + count = Int32(apiChats.count) + case let .chatsSlice(apiCount, apiChats): + chats = apiChats + count = apiCount + } + parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: []) + updatePeers(transaction: transaction, accountPeerId: account.peerId, peers: parsedPeers) + var peers: [EnginePeer] = [] + for chat in chats { + if let peer = transaction.getPeer(chat.peerId) { + peers.append(EnginePeer(peer)) + if case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _) = chat, let participantsCount = participantsCount { + transaction.updatePeerCachedData(peerIds: Set([peer.id]), update: { _, current in + var current = current as? CachedChannelData ?? CachedChannelData() + var participantsSummary = current.participantsSummary + + participantsSummary.memberCount = participantsCount + + current = current.withUpdatedParticipantsSummary(participantsSummary) + return current + }) + } + } + } + if let entry = CodableEntry(CachedRecommendedChannels(peerIds: peers.map(\.id), count: count, isHidden: false)) { + transaction.putItemCacheEntry(id: entryId(peerId: peerId), entry: entry) + } + return peers + } + |> ignoreValues + } + } +} + +public struct RecommendedChannels: Equatable { + public struct Channel: Equatable { + public let peer: EnginePeer + public let subscribers: Int32 + } + + public let channels: [Channel] + public let count: Int32 + public let isHidden: Bool +} + +func _internal_recommendedChannels(account: Account, peerId: EnginePeer.Id) -> Signal { + let key = PostboxViewKey.cachedItem(entryId(peerId: peerId)) + return account.postbox.combinedView(keys: [key]) + |> mapToSignal { views -> Signal in + guard let cachedChannels = (views.views[key] as? CachedItemView)?.value?.get(CachedRecommendedChannels.self), !cachedChannels.peerIds.isEmpty else { + return .single(nil) + } + return account.postbox.multiplePeersView(cachedChannels.peerIds) + |> mapToSignal { view in + return account.postbox.transaction { transaction -> RecommendedChannels? in + var channels: [RecommendedChannels.Channel] = [] + for peerId in cachedChannels.peerIds { + if let peer = view.peers[peerId] as? TelegramChannel, let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData { + if case .member = peer.participationStatus { + } else { + channels.append(RecommendedChannels.Channel(peer: EnginePeer(peer), subscribers: cachedData.participantsSummary.memberCount ?? 0)) + } + } + } + return RecommendedChannels(channels: channels, count: cachedChannels.count, isHidden: cachedChannels.isHidden) + } + } + } +} + +func _internal_toggleRecommendedChannelsHidden(account: Account, peerId: EnginePeer.Id, hidden: Bool) -> Signal { + return account.postbox.transaction { transaction in + if let cachedChannels = transaction.retrieveItemCacheEntry(id: entryId(peerId: peerId))?.get(CachedRecommendedChannels.self) { + if let entry = CodableEntry(CachedRecommendedChannels(peerIds: cachedChannels.peerIds, count: cachedChannels.count, isHidden: hidden)) { + transaction.putItemCacheEntry(id: entryId(peerId: peerId), entry: entry) + } + } + } + |> ignoreValues +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Communities.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Communities.swift index c42442928ed..66ce170d5da 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Communities.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Communities.swift @@ -280,7 +280,7 @@ func _internal_checkChatFolderLink(account: Account, slug: String) -> Signal Signal S var memberCounts: [ChatListFiltersState.ChatListFilterUpdates.MemberCount] = [] for chat in chats { - if case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _) = chat { + if case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _) = chat { if let participantsCount = participantsCount { memberCounts.append(ChatListFiltersState.ChatListFilterUpdates.MemberCount(id: chat.peerId, count: participantsCount)) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift index a24d8954ff2..59d1a1b1738 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift @@ -31,7 +31,7 @@ func _internal_inactiveChannelList(network: Network) -> Signal<[InactiveChannel] var participantsCounts: [PeerId: Int32] = [:] for chat in chats { switch chat { - case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCountValue, _, _, _, _): + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCountValue, _, _, _): if let participantsCountValue = participantsCountValue { participantsCounts[chat.peerId] = participantsCountValue } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/JoinChannel.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/JoinChannel.swift index 2e7e672a4ab..c5971928426 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/JoinChannel.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/JoinChannel.swift @@ -77,6 +77,11 @@ func _internal_joinChannel(account: Account, peerId: PeerId, hash: String?) -> S |> castError(JoinChannelError.self) } } + |> afterCompleted { + if hash == nil { + let _ = _internal_requestRecommendedChannels(account: account, peerId: peerId, forceUpdate: true).startStandalone() + } + } } else { return .fail(.generic) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift index 64deebf284a..8f6fca61b85 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift @@ -510,9 +510,17 @@ public extension EnginePeer { return self._asPeer().nameColor } + var profileColor: PeerNameColor? { + return self._asPeer().profileColor + } + var backgroundEmojiId: Int64? { return self._asPeer().backgroundEmojiId } + + var profileBackgroundEmojiId: Int64? { + return self._asPeer().profileBackgroundEmojiId + } } public extension EnginePeer { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift index bb04ad90247..6fa77daa0b0 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift @@ -38,7 +38,7 @@ public func _internal_searchPeers(accountPeerId: PeerId, postbox: Postbox, netwo for chat in chats { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { switch chat { - case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _): + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _): if let participantsCount = participantsCount { subscribers[groupOrChannel.id] = participantsCount } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 8079defdc8e..64784073a61 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -715,8 +715,8 @@ public extension TelegramEngine { return _internal_updateBotAbout(account: self.account, peerId: peerId, about: about) } - public func updatePeerNameColorAndEmoji(peerId: EnginePeer.Id, nameColor: PeerNameColor, backgroundEmojiId: Int64?) -> Signal { - return _internal_updatePeerNameColorAndEmoji(account: self.account, peerId: peerId, nameColor: nameColor, backgroundEmojiId: backgroundEmojiId) + public func updatePeerNameColorAndEmoji(peerId: EnginePeer.Id, nameColor: PeerNameColor, backgroundEmojiId: Int64?, profileColor: PeerNameColor?, profileBackgroundEmojiId: Int64?) -> Signal { + return _internal_updatePeerNameColorAndEmoji(account: self.account, peerId: peerId, nameColor: nameColor, backgroundEmojiId: backgroundEmojiId, profileColor: profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId) } public func getChatListPeers(filterPredicate: ChatListFilterPredicate) -> Signal<[EnginePeer], NoError> { @@ -1129,6 +1129,37 @@ public extension TelegramEngine { } } + public func updateForumViewAsMessages(peerId: EnginePeer.Id, value: Bool) -> Signal { + return self.account.postbox.transaction { transaction -> Api.InputChannel? in + transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in + if let current = current as? CachedChannelData { + return current.withUpdatedViewForumAsMessages(.known(value)) + } else { + return current + } + }) + + return transaction.getPeer(peerId).flatMap(apiInputChannel) + } + |> mapToSignal { inputChannel -> Signal in + guard let inputChannel = inputChannel else { + return .complete() + } + + return self.account.network.request(Api.functions.channels.toggleViewForumAsMessages(channel: inputChannel, enabled: value ? .boolTrue : .boolFalse)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> beforeNext { updates in + if let updates = updates { + self.account.stateManager.addUpdates(updates) + } + } + |> ignoreValues + } + } + public func exportChatFolder(filterId: Int32, title: String, peerIds: [PeerId]) -> Signal { return _internal_exportChatFolder(account: self.account, filterId: filterId, title: title, peerIds: peerIds) } @@ -1209,6 +1240,18 @@ public extension TelegramEngine { public func applyChannelBoost(peerId: EnginePeer.Id, slots: [Int32]) -> Signal { return _internal_applyChannelBoost(account: self.account, peerId: peerId, slots: slots) } + + public func recommendedChannels(peerId: EnginePeer.Id) -> Signal { + return _internal_recommendedChannels(account: self.account, peerId: peerId) + } + + public func toggleRecommendedChannelsHidden(peerId: EnginePeer.Id, hidden: Bool) -> Signal { + return _internal_toggleRecommendedChannelsHidden(account: self.account, peerId: peerId, hidden: hidden) + } + + public func requestRecommendedChannels(peerId: EnginePeer.Id, forceUpdate: Bool = false) -> Signal { + return _internal_requestRecommendedChannels(account: self.account, peerId: peerId, forceUpdate: forceUpdate) + } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift index c48e6d503a4..3cfeaffb16c 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift @@ -604,6 +604,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee } let membersHidden = (flags2 & (1 << 2)) != 0 + let forumViewAsMessages = (flags2 & (1 << 6)) != 0 return previous.withUpdatedFlags(channelFlags) .withUpdatedAbout(about) @@ -632,6 +633,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee .withUpdatedSendAsPeerId(sendAsPeerId) .withUpdatedAllowedReactions(.known(mappedAllowedReactions)) .withUpdatedMembersHidden(.known(PeerMembersHidden(value: membersHidden))) + .withUpdatedViewForumAsMessages(.known(forumViewAsMessages)) }) if let minAvailableMessageId = minAvailableMessageId, minAvailableMessageIdUpdated { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdatePeerInfo.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdatePeerInfo.swift index f91d9d3fce1..69de7472240 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdatePeerInfo.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdatePeerInfo.swift @@ -94,7 +94,7 @@ public enum UpdatePeerNameColorAndEmojiError { case channelBoostRequired } -func _internal_updatePeerNameColorAndEmoji(account: Account, peerId: EnginePeer.Id, nameColor: PeerNameColor, backgroundEmojiId: Int64?) -> Signal { +func _internal_updatePeerNameColorAndEmoji(account: Account, peerId: EnginePeer.Id, nameColor: PeerNameColor, backgroundEmojiId: Int64?, profileColor: PeerNameColor?, profileBackgroundEmojiId: Int64?) -> Signal { return account.postbox.transaction { transaction -> Signal in if let peer = transaction.getPeer(peerId) { if let peer = peer as? TelegramChannel, let inputChannel = apiInputChannel(peer) { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/BlockedPeersContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/BlockedPeersContext.swift index 87922c03cae..4f74f3c3660 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/BlockedPeersContext.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/BlockedPeersContext.swift @@ -211,7 +211,7 @@ public final class BlockedPeersContext { } } let inputPeers = peers.compactMap { apiInputPeer($0) } - return network.request(Api.functions.contacts.setBlocked(flags: flags, id: inputPeers, limit: Int32(peers.count))) + return network.request(Api.functions.contacts.setBlocked(flags: flags, id: inputPeers, limit: Int32(max(currentPeers.count, peers.count)))) |> mapError { _ -> BlockedPeersContextAddError in return .generic } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift index d53d9d0e50f..53420ef5057 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift @@ -166,7 +166,7 @@ func _internal_requestAccountPrivacySettings(account: Account) -> Signal Sig } } +func _internal_searchEmojiSetsRemotely(postbox: Postbox, network: Network, query: String) -> Signal { + return network.request(Api.functions.messages.searchEmojiStickerSets(flags: 0, q: query, hash: 0)) + |> mapError {_ in} + |> mapToSignal { value in + var index: Int32 = 1000 + switch value { + case let .foundStickerSets(_, sets: sets): + var result = FoundStickerSets() + for set in sets { + let parsed = parsePreviewStickerSet(set, namespace: Namespaces.ItemCollection.CloudEmojiPacks) + let values = parsed.1.map({ ItemCollectionViewEntry(index: ItemCollectionViewEntryIndex(collectionIndex: index, collectionId: parsed.0.id, itemIndex: $0.index), item: $0) }) + result = result.withUpdatedInfosAndEntries(infos: [(parsed.0.id, parsed.0, parsed.1.first, false)], entries: values) + index += 1 + } + return .single(result) + default: + break + } + + return .complete() + } + |> `catch` { _ -> Signal in + return .single(FoundStickerSets()) + } + |> mapToSignal { result -> Signal in + return postbox.combinedView(keys: [PostboxViewKey.itemCollectionInfos(namespaces: [Namespaces.ItemCollection.CloudEmojiPacks])]) + |> map { combinedView -> Set in + var installed = Set() + + if let view = combinedView.views[PostboxViewKey.itemCollectionInfos(namespaces: [Namespaces.ItemCollection.CloudEmojiPacks])] as? ItemCollectionInfosView { + var installedIds = Set() + if let ids = view.entriesByNamespace[Namespaces.ItemCollection.CloudEmojiPacks] { + installedIds = Set(ids.map(\.id)) + } + installed = installedIds.intersection(Set(result.infos.map(\.0))) + } + + return installed + } + |> distinctUntilChanged + |> map { installed -> FoundStickerSets in + return FoundStickerSets(infos: result.infos.map { info in + return (info.0, info.1, info.2, installed.contains(info.0)) + }, entries: result.entries) + } + } +} + func _internal_searchStickerSets(postbox: Postbox, query: String) -> Signal { return postbox.transaction { transaction -> Signal in let infos = transaction.getItemCollectionsInfos(namespace: Namespaces.ItemCollection.CloudStickerPacks) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift index 264528a33e8..54aca5647e2 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift @@ -37,6 +37,10 @@ public extension TelegramEngine { public func searchStickerSetsRemotely(query: String) -> Signal { return _internal_searchStickerSetsRemotely(network: self.account.network, query: query) } + + public func searchEmojiSetsRemotely(query: String) -> Signal { + return _internal_searchEmojiSetsRemotely(postbox: self.account.postbox, network: self.account.network, query: query) + } public func searchStickerSets(query: String) -> Signal { return _internal_searchStickerSets(postbox: self.account.postbox, query: query) @@ -194,6 +198,10 @@ public extension TelegramEngine { } public func _internal_resolveInlineStickers(postbox: Postbox, network: Network, fileIds: [Int64]) -> Signal<[Int64: TelegramMediaFile], NoError> { + if fileIds.isEmpty { + return .single([:]) + } + return postbox.transaction { transaction -> [Int64: TelegramMediaFile] in var cachedFiles: [Int64: TelegramMediaFile] = [:] for fileId in fileIds { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift b/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift index 437097df2de..be6cbd94fb7 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift @@ -123,7 +123,7 @@ public enum SetChatWallpaperError { case flood } -func _internal_setChatWallpaper(postbox: Postbox, network: Network, stateManager: AccountStateManager, peerId: PeerId, wallpaper: TelegramWallpaper?, applyUpdates: Bool = true) -> Signal { +func _internal_setChatWallpaper(postbox: Postbox, network: Network, stateManager: AccountStateManager, peerId: PeerId, wallpaper: TelegramWallpaper?, forBoth: Bool, applyUpdates: Bool = true) -> Signal { return postbox.loadedPeerWithId(peerId) |> castError(SetChatWallpaperError.self) |> mapToSignal { peer in @@ -148,6 +148,9 @@ func _internal_setChatWallpaper(postbox: Postbox, network: Network, stateManager inputWallpaper = inputWallpaperAndInputSettings.0 inputSettings = inputWallpaperAndInputSettings.1 } + if forBoth { + flags |= 1 << 3 + } return network.request(Api.functions.messages.setChatWallPaper(flags: flags, peer: inputPeer, wallpaper: inputWallpaper, settings: inputSettings, id: nil), automaticFloodWait: false) |> mapError { error -> SetChatWallpaperError in if error.errorDescription.hasPrefix("FLOOD_WAIT") { @@ -168,14 +171,54 @@ func _internal_setChatWallpaper(postbox: Postbox, network: Network, stateManager } } -public enum SetExistingChatWallpaperError { +public enum RevertChatWallpaperError { case generic } -func _internal_setExistingChatWallpaper(account: Account, messageId: MessageId, settings: WallpaperSettings?) -> Signal { +func _internal_revertChatWallpaper(account: Account, peerId: EnginePeer.Id) -> Signal { + return account.postbox.loadedPeerWithId(peerId) + |> castError(RevertChatWallpaperError.self) + |> mapToSignal { peer in + guard let inputPeer = apiInputPeer(peer) else { + return .fail(.generic) + } + let flags: Int32 = 1 << 4 + return account.network.request(Api.functions.messages.setChatWallPaper(flags: flags, peer: inputPeer, wallpaper: nil, settings: nil, id: nil), automaticFloodWait: false) + |> map(Optional.init) + |> `catch` { error -> Signal in + if error.errorDescription == "WALLPAPER_NOT_FOUND" { + return .single(nil) + } + return .fail(.generic) + } + |> mapToSignal { updates -> Signal in + if let updates = updates { + account.stateManager.addUpdates(updates) + return .complete() + } else { + return account.postbox.transaction { transaction in + transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in + if let current = current as? CachedUserData { + return current.withUpdatedWallpaper(nil) + } else { + return current + } + }) + } + |> castError(RevertChatWallpaperError.self) + } + } + } +} + +public enum SetExistingChatWallpaperError { + case generic +} + +func _internal_setExistingChatWallpaper(account: Account, messageId: MessageId, settings: WallpaperSettings?, forBoth: Bool) -> Signal { return account.postbox.transaction { transaction -> Peer? in if let peer = transaction.getPeer(messageId.peerId), let message = transaction.getMessage(messageId) { - if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case let .setChatWallpaper(wallpaper) = action.action { + if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case let .setChatWallpaper(wallpaper, _) = action.action { var wallpaper = wallpaper if let settings = settings { wallpaper = wallpaper.withUpdatedSettings(settings) @@ -205,6 +248,9 @@ func _internal_setExistingChatWallpaper(account: Account, messageId: MessageId, flags |= 1 << 2 inputSettings = apiWallpaperSettings(settings) } + if forBoth { + flags |= 1 << 3 + } return account.network.request(Api.functions.messages.setChatWallPaper(flags: flags, peer: inputPeer, wallpaper: nil, settings: inputSettings, id: messageId.id), automaticFloodWait: false) |> `catch` { _ -> Signal in return .fail(.generic) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Themes/TelegramEngineThemes.swift b/submodules/TelegramCore/Sources/TelegramEngine/Themes/TelegramEngineThemes.swift index e189e3cbaa5..8e8ea3b097a 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Themes/TelegramEngineThemes.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Themes/TelegramEngineThemes.swift @@ -17,13 +17,17 @@ public extension TelegramEngine { return _internal_setChatTheme(account: self.account, peerId: peerId, emoticon: emoticon) } - public func setChatWallpaper(peerId: PeerId, wallpaper: TelegramWallpaper?) -> Signal { - return _internal_setChatWallpaper(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, peerId: peerId, wallpaper: wallpaper) + public func setChatWallpaper(peerId: PeerId, wallpaper: TelegramWallpaper?, forBoth: Bool) -> Signal { + return _internal_setChatWallpaper(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, peerId: peerId, wallpaper: wallpaper, forBoth: forBoth) |> ignoreValues } - public func setExistingChatWallpaper(messageId: MessageId, settings: WallpaperSettings?) -> Signal { - return _internal_setExistingChatWallpaper(account: self.account, messageId: messageId, settings: settings) + public func setExistingChatWallpaper(messageId: MessageId, settings: WallpaperSettings?, forBoth: Bool) -> Signal { + return _internal_setExistingChatWallpaper(account: self.account, messageId: messageId, settings: settings, forBoth: forBoth) + } + + public func revertChatWallpaper(peerId: EnginePeer.Id) -> Signal { + return _internal_revertChatWallpaper(account: self.account, peerId: peerId) } } } diff --git a/submodules/TelegramCore/Sources/UpdatePeers.swift b/submodules/TelegramCore/Sources/UpdatePeers.swift index be8a1269404..10d9648716f 100644 --- a/submodules/TelegramCore/Sources/UpdatePeers.swift +++ b/submodules/TelegramCore/Sources/UpdatePeers.swift @@ -72,7 +72,7 @@ func updatePeers(transaction: Transaction, accountPeerId: PeerId, peers: Accumul } for (_, chat) in peers.chats { switch chat { - case let .channel(flags, flags2, _, _, _, _, _, _, _, _, _, _, _, _, storiesMaxId, _, _): + case let .channel(flags, flags2, _, _, _, _, _, _, _, _, _, _, _, _, storiesMaxId, _): let isMin = (flags & (1 << 12)) != 0 let storiesUnavailable = (flags2 & (1 << 3)) != 0 diff --git a/submodules/TelegramCore/Sources/Utils/PeerUtils.swift b/submodules/TelegramCore/Sources/Utils/PeerUtils.swift index e535a3a99d7..5c119bc7138 100644 --- a/submodules/TelegramCore/Sources/Utils/PeerUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/PeerUtils.swift @@ -253,6 +253,17 @@ public extension Peer { } } + var profileColor: PeerNameColor? { + switch self { + case let user as TelegramUser: + return user.profileColor + case let channel as TelegramChannel: + return channel.profileColor + default: + return nil + } + } + var hasCustomNameColor: Bool { let defaultNameColor = PeerNameColor(rawValue: Int32(self.id.id._internalGetInt64Value() % 7)) if self.nameColor != defaultNameColor { @@ -271,6 +282,17 @@ public extension Peer { return nil } } + + var profileBackgroundEmojiId: Int64? { + switch self { + case let user as TelegramUser: + return user.profileBackgroundEmojiId + case let channel as TelegramChannel: + return channel.profileBackgroundEmojiId + default: + return nil + } + } } public extension TelegramPeerUsername { diff --git a/submodules/TelegramNotices/Sources/Notices.swift b/submodules/TelegramNotices/Sources/Notices.swift index 812bae542c3..96bb4d1af12 100644 --- a/submodules/TelegramNotices/Sources/Notices.swift +++ b/submodules/TelegramNotices/Sources/Notices.swift @@ -185,6 +185,9 @@ private enum ApplicationSpecificGlobalNotice: Int32 { case displayStoryInteractionGuide = 51 case dismissedPremiumAppIconsBadge = 52 case replyQuoteTextSelectionTip = 53 + case dismissedPremiumWallpapersBadge = 54 + case dismissedPremiumColorsBadge = 55 + case multipleReactionsSuggestion = 56 var key: ValueBoxKey { let v = ValueBoxKey(length: 4) @@ -450,10 +453,18 @@ private struct ApplicationSpecificNoticeKeys { static func dismissedPremiumAppIconsBadge() -> NoticeEntryKey { return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.dismissedPremiumAppIconsBadge.key) } - static func replyQuoteTextSelectionTip() -> NoticeEntryKey { return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.replyQuoteTextSelectionTip.key) } + static func dismissedPremiumWallpapersBadge() -> NoticeEntryKey { + return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.dismissedPremiumWallpapersBadge.key) + } + static func dismissedPremiumColorsBadge() -> NoticeEntryKey { + return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.dismissedPremiumColorsBadge.key) + } + static func multipleReactionsSuggestion() -> NoticeEntryKey { + return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.multipleReactionsSuggestion.key) + } } public struct ApplicationSpecificNotice { @@ -1772,4 +1783,73 @@ public struct ApplicationSpecificNotice { } |> take(1) } + + public static func setDismissedPremiumWallpapersBadge(accountManager: AccountManager) -> Signal { + return accountManager.transaction { transaction -> Void in + if let entry = CodableEntry(ApplicationSpecificBoolNotice()) { + transaction.setNotice(ApplicationSpecificNoticeKeys.dismissedPremiumWallpapersBadge(), entry) + } + } + |> ignoreValues + } + + public static func dismissedPremiumWallpapersBadge(accountManager: AccountManager) -> Signal { + return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.dismissedPremiumWallpapersBadge()) + |> map { view -> Bool in + if let _ = view.value?.get(ApplicationSpecificBoolNotice.self) { + return true + } else { + return false + } + } + |> take(1) + } + + public static func setDismissedPremiumColorsBadge(accountManager: AccountManager) -> Signal { + return accountManager.transaction { transaction -> Void in + if let entry = CodableEntry(ApplicationSpecificBoolNotice()) { + transaction.setNotice(ApplicationSpecificNoticeKeys.dismissedPremiumColorsBadge(), entry) + } + } + |> ignoreValues + } + + public static func dismissedPremiumColorsBadge(accountManager: AccountManager) -> Signal { + return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.dismissedPremiumColorsBadge()) + |> map { view -> Bool in + if let _ = view.value?.get(ApplicationSpecificBoolNotice.self) { + return true + } else { + return false + } + } + |> take(1) + } + + public static func getMultipleReactionsSuggestion(accountManager: AccountManager) -> Signal { + return accountManager.transaction { transaction -> Int32 in + if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.multipleReactionsSuggestion())?.get(ApplicationSpecificCounterNotice.self) { + return value.value + } else { + return 0 + } + } + } + + public static func incrementMultipleReactionsSuggestion(accountManager: AccountManager, count: Int = 1) -> Signal { + return accountManager.transaction { transaction -> Int in + var currentValue: Int32 = 0 + if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.multipleReactionsSuggestion())?.get(ApplicationSpecificCounterNotice.self) { + currentValue = value.value + } + let previousValue = currentValue + currentValue += Int32(count) + + if let entry = CodableEntry(ApplicationSpecificCounterNotice(value: currentValue)) { + transaction.setNotice(ApplicationSpecificNoticeKeys.multipleReactionsSuggestion(), entry) + } + + return Int(previousValue) + } + } } diff --git a/submodules/TelegramPresentationData/Sources/PresentationTheme.swift b/submodules/TelegramPresentationData/Sources/PresentationTheme.swift index 064c18d7b4e..fb7f9057722 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationTheme.swift @@ -17,6 +17,10 @@ public final class PresentationThemeGradientColors { return (self.topColor, self.bottomColor) } + public var array: [UIColor] { + return [self.topColor, self.bottomColor] + } + public func withUpdated(topColor: UIColor? = nil, bottomColor: UIColor? = nil) -> PresentationThemeGradientColors { return PresentationThemeGradientColors(topColor: topColor ?? self.topColor, bottomColor: bottomColor ?? self.bottomColor) } diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift index c93ae850733..ea072932d0a 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift @@ -72,6 +72,9 @@ public enum PresentationResourceKey: Int32 { case itemListTopicArrowIcon case itemListAddBoostsIcon + case statsReactionsIcon + case statsForwardsIcon + case itemListVoiceCallIcon case itemListVideoCallIcon diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift index 3949ad4c0cd..c25c954ed68 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift @@ -329,4 +329,16 @@ public struct PresentationResourcesItemList { return generateTintedImage(image: UIImage(bundleImageName: "Chat List/TopicArrowIcon"), color: theme.list.itemSecondaryTextColor) }) } + + public static func statsReactionsIcon(_ theme: PresentationTheme) -> UIImage? { + return theme.image(PresentationResourceKey.statsReactionsIcon.rawValue, { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chart/Reactions"), color: theme.list.itemSecondaryTextColor) + }) + } + + public static func statsForwardsIcon(_ theme: PresentationTheme) -> UIImage? { + return theme.image(PresentationResourceKey.statsForwardsIcon.rawValue, { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chart/Forwards"), color: theme.list.itemSecondaryTextColor) + }) + } } diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift index 51c5ae79c93..0018fc54f46 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift @@ -101,7 +101,7 @@ public struct PresentationResourcesRootController { public static func navigationQrCodeIcon(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.navigationQrCodeIcon.rawValue, { theme in - generateTintedImage(image: UIImage(bundleImageName: "Settings/QrIcon"), color: theme.rootController.navigationBar.accentTextColor) + generateTintedImage(image: UIImage(bundleImageName: "Settings/QrIcon"), color: .white) }) } @@ -172,7 +172,7 @@ public struct PresentationResourcesRootController { public static func navigationPostStoryIcon(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.navigationPostStoryIcon.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddStoryIcon"), color: theme.rootController.navigationBar.accentTextColor) + return generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddStoryIcon"), color: .white) }) } } diff --git a/submodules/TelegramStringFormatting/Sources/PresenceStrings.swift b/submodules/TelegramStringFormatting/Sources/PresenceStrings.swift index c563c5da656..b8f5bbd3db9 100644 --- a/submodules/TelegramStringFormatting/Sources/PresenceStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/PresenceStrings.swift @@ -431,13 +431,13 @@ public func stringForRelativeActivityTimestamp(strings: PresentationStrings, dat } } -public func stringForStoryActivityTimestamp(strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, preciseTime: Bool = false, relativeTimestamp: Int32, relativeTo timestamp: Int32) -> String { +public func stringForStoryActivityTimestamp(strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, preciseTime: Bool = false, relativeTimestamp: Int32, relativeTo timestamp: Int32, short: Bool = false) -> String { let difference = timestamp - relativeTimestamp if difference < 60 { - return strings.Time_JustNow + return short ? strings.ShortTime_JustNow : strings.Time_JustNow } else if difference < 60 * 60 { let minutes = difference / 60 - return strings.Time_MinutesAgo(minutes) + return short ? strings.ShortTime_MinutesAgo(minutes) : strings.Time_MinutesAgo(minutes) } else { var t: time_t = time_t(relativeTimestamp) var timeinfo: tm = tm() @@ -454,9 +454,9 @@ public func stringForStoryActivityTimestamp(strings: PresentationStrings, dateTi let dayDifference = timeinfo.tm_yday - timeinfoNow.tm_yday if dayDifference == 0 || dayDifference == -1 { let day: RelativeTimestampFormatDay - if dayDifference == 0 { - let minutes = difference / (60 * 60) - return strings.Time_HoursAgo(minutes) + if dayDifference == 0 || short { + let hours = difference / (60 * 60) + return short ? strings.ShortTime_HoursAgo(hours) : strings.Time_HoursAgo(hours) } else { day = .yesterday } diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index 6e0823ad616..072dbe48e05 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -884,9 +884,15 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, let botName = message.peers[message.id.peerId].flatMap(EnginePeer.init)?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? "" let peerName = message.peers[peerId].flatMap(EnginePeer.init)?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? "" attributedString = addAttributesToStringWithRanges(strings.Notification_RequestedPeer(peerName, botName)._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, peerId), (1, message.id.peerId)])) - case .setChatWallpaper: + case let .setChatWallpaper(_, forBoth): if message.author?.id == accountPeerId { - attributedString = NSAttributedString(string: strings.Notification_YouChangedWallpaper, font: titleFont, textColor: primaryTextColor) + if forBoth { + let peerName = message.peers[message.id.peerId].flatMap(EnginePeer.init)?.compactDisplayTitle ?? "" + let resultTitleString = strings.Notification_YouChangedWallpaperBoth(peerName) + attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes]) + } else { + attributedString = NSAttributedString(string: strings.Notification_YouChangedWallpaper, font: titleFont, textColor: primaryTextColor) + } } else { let resultTitleString = strings.Notification_ChangedWallpaper(compactAuthorName) attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes]) @@ -903,6 +909,21 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, case .giveawayLaunched: let resultTitleString = strings.Notification_GiveawayStarted(compactAuthorName) attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes]) + case .joinedChannel: + attributedString = NSAttributedString(string: strings.Notification_ChannelJoinedByYou, font: titleBoldFont, textColor: primaryTextColor) + case let .giveawayResults(winners, unclaimed): + if winners == 0 { + attributedString = parseMarkdownIntoAttributedString(strings.Notification_GiveawayResultsNoWinners(unclaimed), attributes: MarkdownAttributes(body: bodyAttributes, bold: boldAttributes, link: bodyAttributes, linkAttribute: { _ in return nil })) + } else if unclaimed > 0 { + let winnersString = parseMarkdownIntoAttributedString(strings.Notification_GiveawayResultsMixedWinners(winners), attributes: MarkdownAttributes(body: bodyAttributes, bold: boldAttributes, link: bodyAttributes, linkAttribute: { _ in return nil })) + let unclaimedString = parseMarkdownIntoAttributedString(strings.Notification_GiveawayResultsMixedUnclaimed(unclaimed), attributes: MarkdownAttributes(body: bodyAttributes, bold: boldAttributes, link: bodyAttributes, linkAttribute: { _ in return nil })) + let combinedString = NSMutableAttributedString(attributedString: winnersString) + combinedString.append(NSAttributedString(string: "\n")) + combinedString.append(unclaimedString) + attributedString = combinedString + } else { + attributedString = parseMarkdownIntoAttributedString(strings.Notification_GiveawayResults(winners), attributes: MarkdownAttributes(body: bodyAttributes, bold: boldAttributes, link: bodyAttributes, linkAttribute: { _ in return nil })) + } case .unknown: attributedString = nil } diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index 69f53797f10..c6d56b6baad 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -439,6 +439,13 @@ swift_library( "//submodules/TelegramUI/Components/Chat/ChatLoadingNode", "//submodules/TelegramUI/Components/Settings/PeerNameColorScreen", "//submodules/TelegramUI/Components/ContextMenuScreen", + "//submodules/TelegramUI/Components/PeerAllowedReactionsScreen", + "//submodules/MetalEngine", + "//submodules/TelegramUI/Components/DustEffect", + "//submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen", + "//submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode", + "//submodules/TelegramUI/Components/Chat/ChatQrCodeScreen", + "//submodules/UIKitRuntimeUtils", ] + select({ "@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets, "//build-system:ios_sim_arm64": [], diff --git a/submodules/TelegramUI/Components/AudioTranscriptionButtonComponent/BUILD b/submodules/TelegramUI/Components/AudioTranscriptionButtonComponent/BUILD index 142864139df..b181aca9e7e 100644 --- a/submodules/TelegramUI/Components/AudioTranscriptionButtonComponent/BUILD +++ b/submodules/TelegramUI/Components/AudioTranscriptionButtonComponent/BUILD @@ -15,6 +15,7 @@ swift_library( "//submodules/Display:Display", "//submodules/TelegramPresentationData:TelegramPresentationData", "//submodules/Components/LottieAnimationComponent:LottieAnimationComponent", + "//submodules/Components/BundleIconComponent:BundleIconComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/AudioTranscriptionButtonComponent/Sources/AudioTranscriptionButtonComponent.swift b/submodules/TelegramUI/Components/AudioTranscriptionButtonComponent/Sources/AudioTranscriptionButtonComponent.swift index ad955e5fb76..b614222d278 100644 --- a/submodules/TelegramUI/Components/AudioTranscriptionButtonComponent/Sources/AudioTranscriptionButtonComponent.swift +++ b/submodules/TelegramUI/Components/AudioTranscriptionButtonComponent/Sources/AudioTranscriptionButtonComponent.swift @@ -5,6 +5,7 @@ import AppBundle import Display import TelegramPresentationData import LottieAnimationComponent +import BundleIconComponent public final class AudioTranscriptionButtonComponent: Component { public enum Theme: Equatable { @@ -33,6 +34,7 @@ public final class AudioTranscriptionButtonComponent: Component { case inProgress case expanded case collapsed + case locked } public let theme: AudioTranscriptionButtonComponent.Theme @@ -64,25 +66,21 @@ public final class AudioTranscriptionButtonComponent: Component { private let blurredBackgroundNode: NavigationBackgroundNode private let backgroundLayer: SimpleLayer - private let animationView: ComponentHostView + private var iconView: ComponentView? + private var animationView: ComponentView? private var progressAnimationView: ComponentHostView? override init(frame: CGRect) { self.blurredBackgroundNode = NavigationBackgroundNode(color: .clear) self.backgroundLayer = SimpleLayer() - self.animationView = ComponentHostView() super.init(frame: frame) self.backgroundLayer.masksToBounds = true self.backgroundLayer.cornerRadius = 10.0 self.layer.addSublayer(self.backgroundLayer) - - self.animationView.isUserInteractionEnabled = false - - self.addSubview(self.animationView) - + self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) } @@ -115,58 +113,120 @@ public final class AudioTranscriptionButtonComponent: Component { } if self.component?.transcriptionState != component.transcriptionState { - switch component.transcriptionState { - case .inProgress: - if self.progressAnimationView == nil { - let progressAnimationView = ComponentHostView() - self.progressAnimationView = progressAnimationView - self.addSubview(progressAnimationView) + if case .locked = component.transcriptionState { + if let animationView = self.animationView { + self.animationView = nil + if let view = animationView.view { + view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + view.removeFromSuperview() + }) + } + } + + let iconView: ComponentView + if let current = self.iconView { + iconView = current + } else { + iconView = ComponentView() + self.iconView = iconView + } + + let iconSize = iconView.update( + transition: transition, + component: AnyComponent(BundleIconComponent( + name: "Chat/Message/TranscriptionLocked", + tintColor: foregroundColor + )), + environment: {}, + containerSize: CGSize(width: 30.0, height: 30.0) + ) + + if let view = iconView.view { + if view.superview == nil { + view.isUserInteractionEnabled = false + self.addSubview(view) + } + view.frame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: floor((size.width - iconSize.height) / 2.0)), size: iconSize) } - default: - if let progressAnimationView = self.progressAnimationView { - self.progressAnimationView = nil - if case .none = transition.animation { - progressAnimationView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak progressAnimationView] _ in - progressAnimationView?.removeFromSuperview() + } else { + if let iconView = self.iconView { + self.iconView = nil + if let view = iconView.view { + view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + view.removeFromSuperview() }) - } else { - progressAnimationView.removeFromSuperview() } } + + let animationView: ComponentView + if let current = self.animationView { + animationView = current + } else { + animationView = ComponentView() + self.animationView = animationView + } + + switch component.transcriptionState { + case .inProgress: + if self.progressAnimationView == nil { + let progressAnimationView = ComponentHostView() + self.progressAnimationView = progressAnimationView + self.addSubview(progressAnimationView) + } + default: + if let progressAnimationView = self.progressAnimationView { + self.progressAnimationView = nil + if case .none = transition.animation { + progressAnimationView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak progressAnimationView] _ in + progressAnimationView?.removeFromSuperview() + }) + } else { + progressAnimationView.removeFromSuperview() + } + } + } + + let animationName: String + switch component.transcriptionState { + case .inProgress: + animationName = "voiceToText" + case .collapsed: + animationName = "voiceToText" + case .expanded: + animationName = "textToVoice" + case .locked: + animationName = "voiceToText" + } + let animationSize = animationView.update( + transition: transition, + component: AnyComponent(LottieAnimationComponent( + animation: LottieAnimationComponent.AnimationItem( + name: animationName, + mode: .animateTransitionFromPrevious + ), + colors: [ + "icon.Group 3.Stroke 1": foregroundColor, + "icon.Group 1.Stroke 1": foregroundColor, + "icon.Group 4.Stroke 1": foregroundColor, + "icon.Group 2.Stroke 1": foregroundColor, + "Artboard Copy 2 Outlines.Group 5.Stroke 1": foregroundColor, + "Artboard Copy 2 Outlines.Group 1.Stroke 1": foregroundColor, + "Artboard Copy 2 Outlines.Group 4.Stroke 1": foregroundColor, + "Artboard Copy Outlines.Group 1.Stroke 1": foregroundColor, + ], + size: CGSize(width: 30.0, height: 30.0) + )), + environment: {}, + containerSize: CGSize(width: 30.0, height: 30.0) + ) + if let view = animationView.view { + if view.superview == nil { + view.isUserInteractionEnabled = false + self.addSubview(view) + } + view.frame = CGRect(origin: CGPoint(x: floor((size.width - animationSize.width) / 2.0), y: floor((size.width - animationSize.height) / 2.0)), size: animationSize) + } } - - let animationName: String - switch component.transcriptionState { - case .inProgress: - animationName = "voiceToText" - case .collapsed: - animationName = "voiceToText" - case .expanded: - animationName = "textToVoice" - } - let animationSize = self.animationView.update( - transition: transition, - component: AnyComponent(LottieAnimationComponent( - animation: LottieAnimationComponent.AnimationItem( - name: animationName, - mode: .animateTransitionFromPrevious - ), - colors: [ - "icon.Group 3.Stroke 1": foregroundColor, - "icon.Group 1.Stroke 1": foregroundColor, - "icon.Group 4.Stroke 1": foregroundColor, - "icon.Group 2.Stroke 1": foregroundColor, - "Artboard Copy 2 Outlines.Group 5.Stroke 1": foregroundColor, - "Artboard Copy 2 Outlines.Group 1.Stroke 1": foregroundColor, - "Artboard Copy 2 Outlines.Group 4.Stroke 1": foregroundColor, - "Artboard Copy Outlines.Group 1.Stroke 1": foregroundColor, - ], - size: CGSize(width: 30.0, height: 30.0) - )), - environment: {}, - containerSize: CGSize(width: 30.0, height: 30.0) - ) - self.animationView.frame = CGRect(origin: CGPoint(x: floor((size.width - animationSize.width) / 2.0), y: floor((size.width - animationSize.height) / 2.0)), size: animationSize) } self.backgroundLayer.backgroundColor = backgroundColor.cgColor diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/BUILD b/submodules/TelegramUI/Components/Calls/CallScreen/BUILD new file mode 100644 index 00000000000..6d202c1f728 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/BUILD @@ -0,0 +1,74 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +load( + "@build_bazel_rules_apple//apple:resources.bzl", + "apple_resource_bundle", + "apple_resource_group", +) +load("//build-system/bazel-utils:plist_fragment.bzl", + "plist_fragment", +) + +filegroup( + name = "CallScreenMetalSources", + srcs = glob([ + "Metal/**/*.metal", + ]), + visibility = ["//visibility:public"], +) + +plist_fragment( + name = "CallScreenMetalSourcesBundleInfoPlist", + extension = "plist", + template = + """ + CFBundleIdentifier + org.telegram.CallScreenMetalSources + CFBundleDevelopmentRegion + en + CFBundleName + CallScreen + """ +) + +apple_resource_bundle( + name = "CallScreenMetalSourcesBundle", + infoplists = [ + ":CallScreenMetalSourcesBundleInfoPlist", + ], + resources = [ + ":CallScreenMetalSources", + ], +) + +filegroup( + name = "Assets", + srcs = glob(["CallScreenAssets.xcassets/**"]), + visibility = ["//visibility:public"], +) + +swift_library( + name = "CallScreen", + module_name = "CallScreen", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + data = [ + ":CallScreenMetalSourcesBundle", + ":Assets", + ], + deps = [ + "//submodules/Display", + "//submodules/MetalEngine", + "//submodules/ComponentFlow", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/TelegramUI/Components/AnimatedTextComponent", + "//submodules/AppBundle", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Airpods.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Airpods.imageset/Contents.json new file mode 100644 index 00000000000..0b0b8458325 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Airpods.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_call_audioairpods.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Airpods.imageset/ic_call_audioairpods.pdf b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Airpods.imageset/ic_call_audioairpods.pdf new file mode 100644 index 00000000000..2f31d498cc8 Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Airpods.imageset/ic_call_audioairpods.pdf differ diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsPro.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsPro.imageset/Contents.json new file mode 100644 index 00000000000..370377d8eed --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsPro.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_call_audioairpodspro.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsPro.imageset/ic_call_audioairpodspro.pdf b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsPro.imageset/ic_call_audioairpodspro.pdf new file mode 100644 index 00000000000..305f6ba52bf Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsPro.imageset/ic_call_audioairpodspro.pdf differ diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsProMax.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsProMax.imageset/Contents.json new file mode 100644 index 00000000000..795a18ba911 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsProMax.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_call_airpodsmax.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsProMax.imageset/ic_call_airpodsmax.pdf b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsProMax.imageset/ic_call_airpodsmax.pdf new file mode 100644 index 00000000000..3661b17240c Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsProMax.imageset/ic_call_airpodsmax.pdf differ diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Bluetooth.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Bluetooth.imageset/Contents.json new file mode 100644 index 00000000000..a448d215b71 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Bluetooth.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_call_audiobt.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Bluetooth.imageset/ic_call_audiobt.pdf b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Bluetooth.imageset/ic_call_audiobt.pdf new file mode 100644 index 00000000000..a2f8cad6cf2 Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Bluetooth.imageset/ic_call_audiobt.pdf differ diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Contents.json new file mode 100644 index 00000000000..6e965652df6 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "provides-namespace" : true + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/End.imageset/CallCancelIcon@2x.png b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/End.imageset/CallCancelIcon@2x.png new file mode 100644 index 00000000000..1d51eb17254 Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/End.imageset/CallCancelIcon@2x.png differ diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/End.imageset/CallCancelIcon@3x.png b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/End.imageset/CallCancelIcon@3x.png new file mode 100644 index 00000000000..95e99b4db2e Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/End.imageset/CallCancelIcon@3x.png differ diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/End.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/End.imageset/Contents.json new file mode 100644 index 00000000000..b9550f6ca9f --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/End.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "CallCancelIcon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "CallCancelIcon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Flip.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Flip.imageset/Contents.json new file mode 100644 index 00000000000..5c38ab09661 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Flip.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_flip (1).pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Flip.imageset/ic_flip (1).pdf b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Flip.imageset/ic_flip (1).pdf new file mode 100644 index 00000000000..da091564121 Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Flip.imageset/ic_flip (1).pdf differ diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Mute.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Mute.imageset/Contents.json new file mode 100644 index 00000000000..be885df0640 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Mute.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_call_microphone.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Mute.imageset/ic_call_microphone.pdf b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Mute.imageset/ic_call_microphone.pdf new file mode 100644 index 00000000000..99ad8021939 Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Mute.imageset/ic_call_microphone.pdf differ diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Speaker.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Speaker.imageset/Contents.json new file mode 100644 index 00000000000..8876671fabb --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Speaker.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_call_speaker.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Speaker.imageset/ic_call_speaker.pdf b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Speaker.imageset/ic_call_speaker.pdf new file mode 100644 index 00000000000..104b537f82c Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Speaker.imageset/ic_call_speaker.pdf differ diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Video.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Video.imageset/Contents.json new file mode 100644 index 00000000000..324f180936b --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Video.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_call_camera.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Video.imageset/ic_call_camera.pdf b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Video.imageset/ic_call_camera.pdf new file mode 100644 index 00000000000..92a55e486d0 Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Video.imageset/ic_call_camera.pdf differ diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/VideoGradient.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/VideoGradient.imageset/Contents.json new file mode 100644 index 00000000000..0d1e12bb583 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/VideoGradient.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "smoothGradient 0.4.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/VideoGradient.imageset/smoothGradient 0.4.png b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/VideoGradient.imageset/smoothGradient 0.4.png new file mode 100644 index 00000000000..49a5faf1c2e Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/VideoGradient.imageset/smoothGradient 0.4.png differ diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Contents.json new file mode 100644 index 00000000000..73c00596a7f --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Metal/CallScreenShaders.metal b/submodules/TelegramUI/Components/Calls/CallScreen/Metal/CallScreenShaders.metal new file mode 100644 index 00000000000..928f1106686 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Metal/CallScreenShaders.metal @@ -0,0 +1,347 @@ +#include + +using namespace metal; + +struct Rectangle { + float2 origin; + float2 size; +}; + +constant static float2 quadVertices[6] = { + float2(0.0, 0.0), + float2(1.0, 0.0), + float2(0.0, 1.0), + float2(1.0, 0.0), + float2(0.0, 1.0), + float2(1.0, 1.0) +}; + +struct QuadVertexOut { + float4 position [[position]]; + float2 uv; +}; + +vertex QuadVertexOut callBackgroundVertex( + const device Rectangle &rect [[ buffer(0) ]], + unsigned int vid [[ vertex_id ]] +) { + float2 quadVertex = quadVertices[vid]; + + QuadVertexOut out; + + out.position = float4(rect.origin.x + quadVertex.x * rect.size.x, rect.origin.y + quadVertex.y * rect.size.y, 0.0, 1.0); + out.position.x = -1.0 + out.position.x * 2.0; + out.position.y = -1.0 + out.position.y * 2.0; + + out.uv = quadVertex; + + return out; +} + +half4 rgb2hsv(half4 c) { + half4 K = half4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + half4 p = mix(half4(c.bg, K.wz), half4(c.gb, K.xy), step(c.b, c.g)); + half4 q = mix(half4(p.xyw, c.r), half4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return half4(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x, c.a); +} + +half4 hsv2rgb(half4 c) { + half4 K = half4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + half3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return half4(c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y), c.a); +} + +fragment half4 callBackgroundFragment( + QuadVertexOut in [[stage_in]], + const device float2 *positions [[ buffer(0) ]], + const device float4 *colors [[ buffer(1) ]], + const device float &brightness [[ buffer(2) ]], + const device float &saturation [[ buffer(3) ]], + const device float4 &overlay [[ buffer(4) ]] +) { + half centerDistanceX = in.uv.x - 0.5; + half centerDistanceY = in.uv.y - 0.5; + half centerDistance = distance(half2(in.uv), half2(0.5, 0.5)); + half swirlFactor = 0.35 * centerDistance; + half theta = swirlFactor * swirlFactor * 0.8 * 8.0; + half sinTheta = sin(theta); + half cosTheta = cos(theta); + + half pixelX = max(0.0, min(1.0, 0.5 + centerDistanceX * cosTheta - centerDistanceY * sinTheta)); + half pixelY = max(0.0, min(1.0, 0.5 + centerDistanceX * sinTheta + centerDistanceY * cosTheta)); + + half distanceSum = 0.0; + + half r = 0.0; + half g = 0.0; + half b = 0.0; + + for (int i = 0; i < 4; i++) { + half4 color = half4(colors[i]); + + half2 colorXY = half2(positions[i]); + half2 distanceXY = half2(pixelX - colorXY.x, pixelY - colorXY.y); + + half distance = max(0.0, 0.92 - sqrt(distanceXY.x * distanceXY.x + distanceXY.y * distanceXY.y)); + distance = distance * distance * distance; + distanceSum += distance; + + r = r + distance * color.r; + g = g + distance * color.g; + b = b + distance * color.b; + } + + if (distanceSum < 0.00001) { + distanceSum = 0.00001; + } + + half pixelB = b / distanceSum; + half pixelG = g / distanceSum; + half pixelR = r / distanceSum; + + half4 color(pixelR, pixelG, pixelB, 1.0); + color = rgb2hsv(color); + color.b = clamp(color.b * brightness, 0.0, 1.0); + color.g = clamp(color.g * saturation, 0.0, 1.0); + color = hsv2rgb(color); + color.rgb += half3(overlay.rgb * overlay.a); + color.rgb = min(color.rgb, half3(1.0, 1.0, 1.0)); + + return color; +} + +struct BlobVertexOut { + float4 position [[position]]; +}; + +float2 blobVertex(float2 center, float angle, float radius) { + return float2(center.x + radius * cos(angle), center.y + radius * sin(angle)); +} + +float2 mapPointInRect(Rectangle rect, half2 point) { + half2 out(rect.origin.x + rect.size.x * point.x, rect.origin.y + rect.size.y * point.y); + out.x = -1.0 + out.x * 2.0; + out.y = -1.0 + out.y * 2.0; + return float2(out); +} + +struct SmoothPoint { + half2 point; + half inAngle; + half inLength; + half outAngle; + half outLength; + + half2 smoothIn() { + return smooth(inAngle, inLength); + } + + half2 smoothOut() { + return smooth(outAngle, outLength); + } + +private: + half2 smooth(half angle, half length) { + return half2( + point.x + length * cos(angle), + point.y + length * sin(angle) + ); + } +}; + +half2 evaluateBlobPoint(const device Rectangle &rect, const device float *positions, int index, int count, int subdivisions) { + float position = positions[index]; + float segmentAngle = float(index) / float(count) * 2.0 * 3.1415926; + return half2(blobVertex(float2(0.5, 0.5), segmentAngle, 0.45 + 0.05 * position)); +} + +SmoothPoint evaluateSmoothBlobPoint(const device Rectangle &rect, const device float *positions, int index, int count, int subdivisions) { + int prevIndex = (index - 1) < 0 ? (count - 1) : (index - 1); + int nextIndex = (index + 1) % count; + + half2 prev = evaluateBlobPoint(rect, positions, prevIndex, count, subdivisions); + half2 curr = evaluateBlobPoint(rect, positions, index, count, subdivisions); + half2 next = evaluateBlobPoint(rect, positions, nextIndex, count, subdivisions); + + float dx = next.x - prev.x; + float dy = -next.y + prev.y; + float angle = atan2(dy, dx); + if (angle < 0.0) { + angle = abs(angle); + } else { + angle = 2 * 3.1415926 - angle; + } + + float smoothAngle = (3.1415926 * 2.0) / float(count); + float smoothness = ((4.0 / 3.0) * tan(smoothAngle / 4.0)) / sin(smoothAngle / 2.0) / 2.0; + + SmoothPoint point; + point.point = curr; + point.inAngle = angle + 3.1415926; + point.inLength = smoothness * distance(curr, prev); + point.outAngle = angle; + point.outLength = smoothness * distance(curr, next); + + return point; +} + +half2 evaluateBezierBlobPoint(thread SmoothPoint &curr, thread SmoothPoint &next, half t) { + half oneMinusT = 1.0 - t; + + half2 p0 = curr.point; + half2 p1 = curr.smoothOut(); + half2 p2 = next.smoothIn(); + half2 p3 = next.point; + + return oneMinusT * oneMinusT * oneMinusT * p0 + 3.0 * t * oneMinusT * oneMinusT * p1 + 3.0 * t * t * oneMinusT * p2 + t * t * t * p3; +} + +vertex BlobVertexOut callBlobVertex( + const device Rectangle &rect [[ buffer(0) ]], + const device float *positions [[ buffer(1) ]], + const device int &count [[ buffer(2) ]], + unsigned int vid [[ vertex_id ]] +) { + const int subdivisions = 8; + + int triangleIndex = vid / 3; + + int segmentIndex = triangleIndex / subdivisions; + int nextIndex = (segmentIndex + 1) % count; + + half innerPosition = half(triangleIndex - segmentIndex * subdivisions) / half(subdivisions); + half nextInnerPosition = half(triangleIndex + 1 - segmentIndex * subdivisions) / half(subdivisions); + + SmoothPoint curr = evaluateSmoothBlobPoint(rect, positions, segmentIndex, count, subdivisions); + SmoothPoint next = evaluateSmoothBlobPoint(rect, positions, nextIndex, count, subdivisions); + + half2 triangle[3]; + triangle[0] = half2(0.5, 0.5); + triangle[1] = evaluateBezierBlobPoint(curr, next, innerPosition); + triangle[2] = evaluateBezierBlobPoint(curr, next, nextInnerPosition); + + BlobVertexOut out; + out.position = float4(float2(mapPointInRect(rect, triangle[vid % 3])), 0.0, 1.0); + + return out; +} + +fragment half4 callBlobFragment( + BlobVertexOut in [[stage_in]] +) { + half alpha = 0.15; + return half4(1.0 * alpha, 1.0 * alpha, 1.0 * alpha, alpha); +} + +kernel void videoYUVToRGBA( + texture2d inTextureY [[ texture(0) ]], + texture2d inTextureUV [[ texture(1) ]], + texture2d outTexture [[ texture(2) ]], + uint2 threadPosition [[ thread_position_in_grid ]] +) { + half y = inTextureY.read(threadPosition).r; + half2 uv = inTextureUV.read(uint2(threadPosition.x / 2, threadPosition.y / 2)).rg - half2(0.5, 0.5); + + half4 color(y + 1.403 * uv.y, y - 0.344 * uv.x - 0.714 * uv.y, y + 1.770 * uv.x, 1.0); + outTexture.write(color, threadPosition); +} + +vertex QuadVertexOut mainVideoVertex( + const device Rectangle &rect [[ buffer(0) ]], + unsigned int vid [[ vertex_id ]] +) { + float2 quadVertex = quadVertices[vid]; + + QuadVertexOut out; + + out.position = float4(rect.origin.x + quadVertex.x * rect.size.x, rect.origin.y + quadVertex.y * rect.size.y, 0.0, 1.0); + out.position.x = -1.0 + out.position.x * 2.0; + out.position.y = -1.0 + out.position.y * 2.0; + + out.uv = float2(quadVertex.x, 1.0 - quadVertex.y); + + return out; +} + +fragment half4 mainVideoFragment( + QuadVertexOut in [[stage_in]], + texture2d texture [[ texture(0) ]], + const device float &brightness [[ buffer(0) ]], + const device float &saturation [[ buffer(1) ]], + const device float4 &overlay [[ buffer(2) ]] +) { + constexpr sampler sampler(coord::normalized, address::repeat, filter::linear); + half4 color = texture.sample(sampler, in.uv); + color = rgb2hsv(color); + color.b = clamp(color.b * brightness, 0.0, 1.0); + color.g = clamp(color.g * saturation, 0.0, 1.0); + color = hsv2rgb(color); + color.rgb += half3(overlay.rgb * overlay.a); + color.rgb = min(color.rgb, half3(1.0, 1.0, 1.0)); + + return half4(color.r, color.g, color.b, color.a); +} + +constant int BLUR_SAMPLE_COUNT = 7; +constant float BLUR_OFFSETS[BLUR_SAMPLE_COUNT] = { + -5.227545617192816, + -3.3147990233346842, + -1.4174297935376852, + 0.47225076494548685, + 2.364576440741639, + 4.268941421369995, + 6 +}; + +constant float BLUR_WEIGHTS[BLUR_SAMPLE_COUNT] = { + 0.015167713616041436, + 0.10117053983645591, + 0.2894431725427234, + 0.3570581167968804, + 0.19014435646109845, + 0.0435647539906345, + 0.0034513467561660305 +}; + +static void gaussianBlur( + texture2d inTexture, + texture2d outTexture, + float2 offset, + uint2 gid +) { + constexpr sampler sampler(coord::normalized, address::clamp_to_edge, filter::linear); + + uint2 textureDim(outTexture.get_width(), outTexture.get_height()); + if(all(gid < textureDim)) { + float3 outColor(0.0); + + float2 size(inTexture.get_width(), inTexture.get_height()); + + float2 baseTexCoord = float2(gid); + + for (int i = 0; i < BLUR_SAMPLE_COUNT; i++) { + outColor += float3(inTexture.sample(sampler, (baseTexCoord + offset * BLUR_OFFSETS[i]) / size).rgb) * BLUR_WEIGHTS[i]; + } + + outTexture.write(half4(half3(outColor), 1.0), gid); + } +} + +kernel void gaussianBlurHorizontal( + texture2d inTexture [[ texture(0) ]], + texture2d outTexture [[ texture(1) ]], + uint2 gid [[ thread_position_in_grid ]] +) { + gaussianBlur(inTexture, outTexture, float2(1, 0), gid); +} + +kernel void gaussianBlurVertical( + texture2d inTexture [[ texture(0) ]], + texture2d outTexture [[ texture(1) ]], + uint2 gid [[ thread_position_in_grid ]] +) { + gaussianBlur(inTexture, outTexture, float2(0, 1), gid); +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Animation/Animation.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Animation/Animation.swift new file mode 100644 index 00000000000..daa9a69fdca --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Animation/Animation.swift @@ -0,0 +1,170 @@ +import Foundation +import UIKit + +public enum AnimationCurve { + case linear + case easeInOut + case spring +} + +extension AnimationCurve { + func map(_ fraction: CGFloat) -> CGFloat { + switch self { + case .linear: + return fraction + case .easeInOut: + return bezierPoint(0.42, 0.0, 0.58, 1.0, fraction) + case .spring: + return bezierPoint(0.23, 1.0, 0.32, 1.0, fraction) + } + } +} + +open class AnyAnimation { +} + +open class AnimationInterpolator { + private let impl: (T, T, CGFloat) -> T + + init(_ impl: @escaping (T, T, CGFloat) -> T) { + self.impl = impl + } + + public func interpolate(from: T, to: T, fraction: CGFloat) -> T { + return self.impl(from, to, fraction) + } +} + +public protocol AnimationInterpolatable { + static var animationInterpolator: AnimationInterpolator { get } +} + +private let CGFloatInterpolator = AnimationInterpolator { from, to, fraction in + return from * (1.0 - fraction) + to * fraction +} +extension CGFloat: AnimationInterpolatable { + public static var animationInterpolator: AnimationInterpolator { + return CGFloatInterpolator + } +} + +private let CGPointInterpolator = AnimationInterpolator { from, to, fraction in + return CGPoint( + x: CGFloatInterpolator.interpolate(from: from.x, to: to.x, fraction: fraction), + y: CGFloatInterpolator.interpolate(from: from.y, to: to.y, fraction: fraction) + ) +} +extension CGPoint: AnimationInterpolatable { + public static var animationInterpolator: AnimationInterpolator { + return CGPointInterpolator + } +} + +#if targetEnvironment(simulator) +@_silgen_name("UIAnimationDragCoefficient") func UIAnimationDragCoefficient() -> Float +#endif + +public final class Animation: AnyAnimation { + private let from: T + private let to: T + private let duration: Double + private let curve: AnimationCurve + private let interpolator: AnimationInterpolator + + private var startTime: Double? + public private(set) var isFinished: Bool = false + + var didStart: (() -> Void)? + + public init(from: T, to: T, duration: Double, curve: AnimationCurve) { + self.from = from + self.to = to + #if targetEnvironment(simulator) + self.duration = duration * Double(UIAnimationDragCoefficient()) + #else + self.duration = duration + #endif + self.curve = curve + self.interpolator = T.animationInterpolator + } + + func start() { + self.startTime = CACurrentMediaTime() + } + + func update(at timestamp: Double) -> T { + guard let startTime = self.startTime else { + return self.from + } + if self.isFinished { + return self.to + } + let fraction = max(0.0, min(1.0, (timestamp - startTime) / self.duration)) + if timestamp > startTime + self.duration { + self.isFinished = true + } + if fraction >= 1.0 { + return self.to + } + return self.interpolator.interpolate(from: self.from, to: self.to, fraction: self.curve.map(fraction)) + } +} + +public class AnyAnimatedProperty { + var didStartAnimation: (() -> Void)? + var hasRunningAnimation: Bool { + return false + } + + public func update() { + } +} + +public final class AnimatedProperty: AnyAnimatedProperty { + private var animation: Animation? + + override var hasRunningAnimation: Bool { + return self.animation != nil + } + + public private(set) var value: T + + public init(_ value: T) { + self.value = value + } + + public func animate(to: T, duration: Double, curve: AnimationCurve) { + let timestamp = CACurrentMediaTime() + + let fromValue: T + if let animation = self.animation { + fromValue = animation.update(at: timestamp) + } else { + fromValue = self.value + } + self.animation = Animation(from: fromValue, to: to, duration: duration, curve: curve) + self.animation?.start() + self.didStartAnimation?() + } + + public func animate(from: T, to: T, duration: Double, curve: AnimationCurve) { + self.value = from + self.animation = Animation(from: from, to: to, duration: duration, curve: curve) + self.animation?.start() + self.didStartAnimation?() + } + + public func set(to: T) { + self.animation = nil + self.value = to + } + + override public func update() { + if let animation = self.animation { + self.value = animation.update(at: CACurrentMediaTime()) + if animation.isFinished { + self.animation = nil + } + } + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Animation/AnimationManager.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Animation/AnimationManager.swift new file mode 100644 index 00000000000..178182b5f1c --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Animation/AnimationManager.swift @@ -0,0 +1,51 @@ +import Foundation +import UIKit +import Display + +public final class ManagedAnimations { + private var displayLinkSubscription: SharedDisplayLinkDriver.Link? + + private var properties: [AnyAnimatedProperty] = [] + + public var updated: (() -> Void)? + + public init() { + } + + public func add(property: AnyAnimatedProperty) { + self.properties.append(property) + property.didStartAnimation = { [weak self] in + guard let self else { + return + } + self.updateNeedAnimations() + } + } + + private func updateNeedAnimations() { + if self.displayLinkSubscription == nil { + self.displayLinkSubscription = SharedDisplayLinkDriver.shared.add { [weak self] _ in + guard let self else { + return + } + self.update() + } + } + } + + private func update() { + var hasRunningAnimations = false + for property in self.properties { + property.update() + if property.hasRunningAnimation { + hasRunningAnimations = true + } + } + + if !hasRunningAnimations { + self.displayLinkSubscription = nil + } + + self.updated?() + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Animation/Bezier.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Animation/Bezier.swift new file mode 100644 index 00000000000..4cc8562b9e7 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Animation/Bezier.swift @@ -0,0 +1,53 @@ +import Foundation + +private func a(_ a1: CGFloat, _ a2: CGFloat) -> CGFloat +{ + return 1.0 - 3.0 * a2 + 3.0 * a1 +} + +private func b(_ a1: CGFloat, _ a2: CGFloat) -> CGFloat +{ + return 3.0 * a2 - 6.0 * a1 +} + +private func c(_ a1: CGFloat) -> CGFloat +{ + return 3.0 * a1 +} + +private func calcBezier(_ t: CGFloat, _ a1: CGFloat, _ a2: CGFloat) -> CGFloat +{ + return ((a(a1, a2)*t + b(a1, a2))*t + c(a1)) * t +} + +private func calcSlope(_ t: CGFloat, _ a1: CGFloat, _ a2: CGFloat) -> CGFloat +{ + return 3.0 * a(a1, a2) * t * t + 2.0 * b(a1, a2) * t + c(a1) +} + +private func getTForX(_ x: CGFloat, _ x1: CGFloat, _ x2: CGFloat) -> CGFloat { + var t = x + var i = 0 + while i < 4 { + let currentSlope = calcSlope(t, x1, x2) + if currentSlope == 0.0 { + return t + } else { + let currentX = calcBezier(t, x1, x2) - x + t -= currentX / currentSlope + } + + i += 1 + } + + return t +} + +public func bezierPoint(_ x1: CGFloat, _ y1: CGFloat, _ x2: CGFloat, _ y2: CGFloat, _ x: CGFloat) -> CGFloat +{ + var value = calcBezier(getTForX(x, x1, x2), y1, y2) + if value >= 0.997 { + value = 1.0 + } + return value +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/CallScreen.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/CallScreen.swift new file mode 100644 index 00000000000..6566d2eb640 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/CallScreen.swift @@ -0,0 +1,28 @@ +import Foundation +import UIKit +import Display +import MetalKit + +private final class BundleMarker: NSObject { +} + +private var metalLibraryValue: MTLLibrary? +func metalLibrary(device: MTLDevice) -> MTLLibrary? { + if let metalLibraryValue { + return metalLibraryValue + } + + let mainBundle = Bundle(for: BundleMarker.self) + guard let path = mainBundle.path(forResource: "CallScreenMetalSourcesBundle", ofType: "bundle") else { + return nil + } + guard let bundle = Bundle(path: path) else { + return nil + } + guard let library = try? device.makeDefaultLibrary(bundle: bundle) else { + return nil + } + + metalLibraryValue = library + return library +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/AvatarLayer.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/AvatarLayer.swift new file mode 100644 index 00000000000..d08c25a5fc7 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/AvatarLayer.swift @@ -0,0 +1,103 @@ +import Foundation +import UIKit +import Display +import ComponentFlow + +final class AvatarLayer: SimpleLayer { + struct Params: Equatable { + var size: CGSize + var cornerRadius: CGFloat + var isExpanded: Bool + + init(size: CGSize, cornerRadius: CGFloat, isExpanded: Bool) { + self.size = size + self.cornerRadius = cornerRadius + self.isExpanded = isExpanded + } + } + + private(set) var params: Params? + private var rasterizedImage: UIImage? + private var isAnimating: Bool = false + + var image: UIImage? { + didSet { + if self.image !== oldValue { + self.updateImage() + } + } + } + + override init() { + super.init() + + self.contentsGravity = .resizeAspectFill + self.masksToBounds = true + } + + override init(layer: Any) { + super.init(layer: layer) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func updateImage() { + guard let params else { + return + } + + if self.isAnimating || params.isExpanded { + self.contents = self.image?.cgImage + } else { + self.contents = self.image.flatMap({ image -> UIImage? in + let imageSize = CGSize(width: min(params.size.width, params.size.height), height: min(params.size.width, params.size.height)) + + return generateImage(imageSize, contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + if params.cornerRadius == size.width * 0.5 { + context.addEllipse(in: CGRect(origin: CGPoint(), size: size)) + } else { + context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: params.cornerRadius).cgPath) + } + context.clip() + + if let cgImage = image.cgImage { + context.draw(cgImage, in: CGRect(origin: CGPoint(), size: size)) + } + }) + })?.cgImage + } + } + + func update(size: CGSize, isExpanded: Bool, cornerRadius: CGFloat, transition: Transition) { + let params = Params(size: size, cornerRadius: cornerRadius, isExpanded: isExpanded) + if self.params == params { + return + } + let previousCornerRadius = self.params?.cornerRadius + self.params = params + + if previousCornerRadius != params.cornerRadius { + self.masksToBounds = true + self.isAnimating = true + self.updateImage() + + if let previousCornerRadius, self.animation(forKey: "cornerRadius") == nil { + self.cornerRadius = previousCornerRadius + } + transition.setCornerRadius(layer: self, cornerRadius: cornerRadius, completion: { [weak self] completed in + guard let self, completed else { + return + } + + self.isAnimating = false + self.masksToBounds = false + self.cornerRadius = 0.0 + + self.updateImage() + }) + } + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ButtonGroupView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ButtonGroupView.swift new file mode 100644 index 00000000000..8eddb2ae024 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ButtonGroupView.swift @@ -0,0 +1,141 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import AppBundle + +final class ButtonGroupView: OverlayMaskContainerView { + final class Button { + enum Content: Equatable { + enum Key: Hashable { + case speaker + case flipCamera + case video + case microphone + case end + } + + case speaker(isActive: Bool) + case flipCamera + case video(isActive: Bool) + case microphone(isMuted: Bool) + case end + + var key: Key { + switch self { + case .speaker: + return .speaker + case .flipCamera: + return .flipCamera + case .video: + return .video + case .microphone: + return .microphone + case .end: + return .end + } + } + } + + let content: Content + let action: () -> Void + + init(content: Content, action: @escaping () -> Void) { + self.content = content + self.action = action + } + } + + private var buttons: [Button]? + private var buttonViews: [Button.Content.Key: ContentOverlayButton] = [:] + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(size: CGSize, buttons: [Button], transition: Transition) { + self.buttons = buttons + + let buttonSize: CGFloat = 56.0 + let buttonSpacing: CGFloat = 36.0 + + let buttonY: CGFloat = size.height - 86.0 - buttonSize + var buttonX: CGFloat = floor((size.width - buttonSize * CGFloat(buttons.count) - buttonSpacing * CGFloat(buttons.count - 1)) * 0.5) + + for button in buttons { + let title: String + let image: UIImage? + let isActive: Bool + var isDestructive: Bool = false + switch button.content { + case let .speaker(isActiveValue): + title = "speaker" + image = UIImage(bundleImageName: "Call/Speaker") + isActive = isActiveValue + case .flipCamera: + title = "flip" + image = UIImage(bundleImageName: "Call/Flip") + isActive = false + case let .video(isActiveValue): + title = "video" + image = UIImage(bundleImageName: "Call/Video") + isActive = isActiveValue + case let .microphone(isActiveValue): + title = "mute" + image = UIImage(bundleImageName: "Call/Mute") + isActive = isActiveValue + case .end: + title = "end" + image = UIImage(bundleImageName: "Call/End") + isActive = false + isDestructive = true + } + + var buttonTransition = transition + let buttonView: ContentOverlayButton + if let current = self.buttonViews[button.content.key] { + buttonView = current + } else { + buttonTransition = transition.withAnimation(.none) + buttonView = ContentOverlayButton(frame: CGRect()) + self.addSubview(buttonView) + self.buttonViews[button.content.key] = buttonView + + let key = button.content.key + buttonView.action = { [weak self] in + guard let self, let button = self.buttons?.first(where: { $0.content.key == key }) else { + return + } + button.action() + } + + Transition.immediate.setScale(view: buttonView, scale: 0.001) + buttonView.alpha = 0.0 + transition.setScale(view: buttonView, scale: 1.0) + transition.setAlpha(view: buttonView, alpha: 1.0) + } + + buttonTransition.setFrame(view: buttonView, frame: CGRect(origin: CGPoint(x: buttonX, y: buttonY), size: CGSize(width: buttonSize, height: buttonSize))) + buttonView.update(size: CGSize(width: buttonSize, height: buttonSize), image: image, isSelected: isActive, isDestructive: isDestructive, title: title, transition: buttonTransition) + buttonX += buttonSize + buttonSpacing + } + + var removeKeys: [Button.Content.Key] = [] + for (key, buttonView) in self.buttonViews { + if !buttons.contains(where: { $0.content.key == key }) { + removeKeys.append(key) + transition.setScale(view: buttonView, scale: 0.001) + transition.setAlpha(view: buttonView, alpha: 0.0, completion: { [weak buttonView] _ in + buttonView?.removeFromSuperview() + }) + } + } + for key in removeKeys { + self.buttonViews.removeValue(forKey: key) + } + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/CallBackgroundLayer.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/CallBackgroundLayer.swift new file mode 100644 index 00000000000..8174757243f --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/CallBackgroundLayer.swift @@ -0,0 +1,220 @@ +import Foundation +import MetalKit +import UIKit +import MetalEngine +import ComponentFlow +import Display + +private func shiftArray(array: [SIMD2], offset: Int) -> [SIMD2] { + var newArray = array + var offset = offset + while offset > 0 { + let element = newArray.removeFirst() + newArray.append(element) + offset -= 1 + } + return newArray +} + +private func gatherPositions(_ list: [SIMD2]) -> [SIMD2] { + var result: [SIMD2] = [] + for i in 0 ..< list.count / 2 { + result.append(list[i * 2]) + } + return result +} + +private func hexToFloat(_ hex: Int) -> SIMD4 { + let red = Float((hex >> 16) & 0xFF) / 255.0 + let green = Float((hex >> 8) & 0xFF) / 255.0 + let blue = Float((hex >> 0) & 0xFF) / 255.0 + return SIMD4(x: red, y: green, z: blue, w: 1.0) +} + +private struct ColorSet: Equatable, AnimationInterpolatable { + static let animationInterpolator = AnimationInterpolator { from, to, fraction in + var result: [SIMD4] = [] + for i in 0 ..< min(from.colors.count, to.colors.count) { + result.append(from.colors[i] * Float(1.0 - fraction) + to.colors[i] * Float(fraction)) + } + return ColorSet(colors: result) + } + + var colors: [SIMD4] +} + +final class CallBackgroundLayer: MetalEngineSubjectLayer, MetalEngineSubject { + var internalData: MetalEngineSubjectInternalData? + + final class RenderState: RenderToLayerState { + let pipelineState: MTLRenderPipelineState + + init?(device: MTLDevice) { + guard let library = metalLibrary(device: device) else { + return nil + } + guard let vertexFunction = library.makeFunction(name: "callBackgroundVertex"), let fragmentFunction = library.makeFunction(name: "callBackgroundFragment") else { + return nil + } + + let pipelineDescriptor = MTLRenderPipelineDescriptor() + pipelineDescriptor.vertexFunction = vertexFunction + pipelineDescriptor.fragmentFunction = fragmentFunction + pipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm + guard let pipelineState = try? device.makeRenderPipelineState(descriptor: pipelineDescriptor) else { + return nil + } + self.pipelineState = pipelineState + } + } + + private static var basePositions: [SIMD2] = [ + SIMD2(x: 0.80, y: 0.10), + SIMD2(x: 0.60, y: 0.20), + SIMD2(x: 0.35, y: 0.25), + SIMD2(x: 0.25, y: 0.60), + SIMD2(x: 0.20, y: 0.90), + SIMD2(x: 0.40, y: 0.80), + SIMD2(x: 0.65, y: 0.75), + SIMD2(x: 0.75, y: 0.40) + ] + + let blurredLayer: MetalEngineSubjectLayer + + private var phase: Float = 0.0 + + private var displayLinkSubscription: SharedDisplayLinkDriver.Link? + + var renderSpec: RenderLayerSpec? { + didSet { + if self.renderSpec != oldValue { + self.setNeedsUpdate() + } + } + } + + private let colorSets: [ColorSet] + private let colorTransition: AnimatedProperty + private var stateIndex: Int = 0 + private let phaseAcceleration = AnimatedProperty(0.0) + + override init() { + self.blurredLayer = MetalEngineSubjectLayer() + + self.colorSets = [ + ColorSet(colors: [ + hexToFloat(0x568FD6), + hexToFloat(0x626ED5), + hexToFloat(0xA667D5), + hexToFloat(0x7664DA) + ]), + ColorSet(colors: [ + hexToFloat(0xACBD65), + hexToFloat(0x459F8D), + hexToFloat(0x53A4D1), + hexToFloat(0x3E917A) + ]), + ColorSet(colors: [ + hexToFloat(0xC0508D), + hexToFloat(0xF09536), + hexToFloat(0xCE5081), + hexToFloat(0xFC7C4C) + ]) + ] + self.colorTransition = AnimatedProperty(colorSets[0]) + + super.init() + + self.didEnterHierarchy = { [weak self] in + guard let self else { + return + } + self.displayLinkSubscription = SharedDisplayLinkDriver.shared.add(framesPerSecond: .fps(30), { [weak self] timeDelta in + guard let self else { + return + } + self.colorTransition.update() + self.phaseAcceleration.update() + + let stepCount = 8 + var phaseStep: CGFloat = 0.5 * timeDelta + phaseStep += phaseStep * self.phaseAcceleration.value * 0.5 + self.phase = (self.phase + Float(phaseStep)).truncatingRemainder(dividingBy: Float(stepCount)) + + self.setNeedsUpdate() + }) + } + self.didExitHierarchy = { [weak self] in + guard let self else { + return + } + self.displayLinkSubscription = nil + } + } + + override init(layer: Any) { + self.blurredLayer = MetalEngineSubjectLayer() + self.colorSets = [] + self.colorTransition = AnimatedProperty(ColorSet(colors: [])) + + super.init(layer: layer) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(stateIndex: Int, transition: Transition) { + if self.stateIndex != stateIndex { + self.stateIndex = stateIndex + if !transition.animation.isImmediate { + self.phaseAcceleration.animate(from: 1.0, to: 0.0, duration: 2.0, curve: .easeInOut) + self.colorTransition.animate(to: self.colorSets[stateIndex % self.colorSets.count], duration: 0.3, curve: .easeInOut) + } else { + self.colorTransition.set(to: self.colorSets[stateIndex % self.colorSets.count]) + } + self.setNeedsUpdate() + } + } + + func update(context: MetalEngineSubjectContext) { + guard let renderSpec = self.renderSpec else { + return + } + + let phase = self.phase + + for i in 0 ..< 2 { + let isBlur = i == 1 + context.renderToLayer(spec: renderSpec, state: RenderState.self, layer: i == 0 ? self : self.blurredLayer, commands: { encoder, placement in + let effectiveRect = placement.effectiveRect + + var rect = SIMD4(Float(effectiveRect.minX), Float(effectiveRect.minY), Float(effectiveRect.width), Float(effectiveRect.height)) + encoder.setVertexBytes(&rect, length: 4 * 4, index: 0) + + let baseStep = floor(phase) + let nextStepInterpolation = phase - floor(phase) + + let positions0 = gatherPositions(shiftArray(array: CallBackgroundLayer.basePositions, offset: Int(baseStep))) + let positions1 = gatherPositions(shiftArray(array: CallBackgroundLayer.basePositions, offset: Int(baseStep) + 1)) + var positions = Array>(repeating: SIMD2(), count: 4) + for i in 0 ..< 4 { + positions[i] = interpolatePoints(positions0[i], positions1[i], at: nextStepInterpolation) + } + encoder.setFragmentBytes(&positions, length: 4 * MemoryLayout>.size, index: 0) + + var colors: [SIMD4] = self.colorTransition.value.colors + + encoder.setFragmentBytes(&colors, length: 4 * MemoryLayout>.size, index: 1) + var brightness: Float = isBlur ? 0.9 : 1.0 + var saturation: Float = isBlur ? 1.1 : 1.0 + var overlay: SIMD4 = isBlur ? SIMD4(1.0, 1.0, 1.0, 0.2) : SIMD4() + encoder.setFragmentBytes(&brightness, length: 4, index: 2) + encoder.setFragmentBytes(&saturation, length: 4, index: 3) + encoder.setFragmentBytes(&overlay, length: 4 * 4, index: 4) + + encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6) + }) + } + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/CallBlobsLayer.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/CallBlobsLayer.swift new file mode 100644 index 00000000000..3107c9d0933 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/CallBlobsLayer.swift @@ -0,0 +1,156 @@ +import Foundation +import MetalKit +import MetalEngine +import Display + +final class CallBlobsLayer: MetalEngineSubjectLayer, MetalEngineSubject { + var internalData: MetalEngineSubjectInternalData? + + struct Blob { + var points: [Float] + var nextPoints: [Float] + + init(count: Int) { + self.points = (0 ..< count).map { _ in + Float.random(in: 0.0 ... 1.0) + } + self.nextPoints = (0 ..< count).map { _ in + Float.random(in: 0.0 ... 1.0) + } + } + + func interpolate(at t: Float) -> [Float] { + var points: [Float] = Array(repeating: 0.0, count: self.points.count) + for i in 0 ..< self.points.count { + points[i] = interpolateFloat(self.points[i], self.nextPoints[i], at: t) + } + return points + } + + mutating func advance() { + self.points = self.nextPoints + self.nextPoints = (0 ..< self.points.count).map { _ in + Float.random(in: 0.0 ... 1.0) + } + } + } + + final class RenderState: RenderToLayerState { + final class Input { + let rect: CGRect + let blobs: [Blob] + let phase: Float + + init(rect: CGRect, blobs: [Blob], phase: Float) { + self.rect = rect + self.blobs = blobs + self.phase = phase + } + } + + let pipelineState: MTLRenderPipelineState + + required init?(device: MTLDevice) { + guard let library = metalLibrary(device: device) else { + return nil + } + guard let vertexFunction = library.makeFunction(name: "callBlobVertex"), let fragmentFunction = library.makeFunction(name: "callBlobFragment") else { + return nil + } + + let pipelineDescriptor = MTLRenderPipelineDescriptor() + pipelineDescriptor.vertexFunction = vertexFunction + pipelineDescriptor.fragmentFunction = fragmentFunction + pipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm + pipelineDescriptor.colorAttachments[0].isBlendingEnabled = true + pipelineDescriptor.colorAttachments[0].rgbBlendOperation = .add + pipelineDescriptor.colorAttachments[0].alphaBlendOperation = .add + pipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = .one + pipelineDescriptor.colorAttachments[0].sourceAlphaBlendFactor = .one + pipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha + pipelineDescriptor.colorAttachments[0].destinationAlphaBlendFactor = .one + + guard let pipelineState = try? device.makeRenderPipelineState(descriptor: pipelineDescriptor) else { + return nil + } + self.pipelineState = pipelineState + } + } + + private var phase: Float = 0.0 + + private var blobs: [Blob] = [] + + private var displayLinkSubscription: SharedDisplayLinkDriver.Link? + + override init() { + super.init() + + self.didEnterHierarchy = { [weak self] in + guard let self else { + return + } + self.displayLinkSubscription = SharedDisplayLinkDriver.shared.add(framesPerSecond: .fps(30), { [weak self] deltaTime in + guard let self else { + return + } + self.phase += 3.0 * Float(deltaTime) + if self.phase >= 1.0 { + for i in 0 ..< self.blobs.count { + self.blobs[i].advance() + } + } + self.phase = self.phase.truncatingRemainder(dividingBy: 1.0) + self.setNeedsUpdate() + }) + } + self.didExitHierarchy = { [weak self] in + guard let self else { + return + } + self.displayLinkSubscription = nil + } + + self.isOpaque = false + self.blobs = (0 ..< 2).map { _ in + Blob(count: 8) + } + } + + override init(layer: Any) { + super.init(layer: layer) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(context: MetalEngineSubjectContext) { + if self.bounds.isEmpty { + return + } + + let phase = self.phase + let blobs = self.blobs + + context.renderToLayer(spec: RenderLayerSpec(size: RenderSize(width: Int(self.bounds.width * 3.0), height: Int(self.bounds.height * 3.0))), state: RenderState.self, layer: self, commands: { encoder, placement in + let rect = placement.effectiveRect + + for i in 0 ..< blobs.count { + var points = blobs[i].interpolate(at: phase) + var count: Int32 = Int32(points.count) + + let insetFraction: CGFloat = CGFloat(i) * 0.1 + + let blobRect = rect.insetBy(dx: insetFraction * 0.5 * rect.width, dy: insetFraction * 0.5 * rect.height) + var rect = SIMD4(Float(blobRect.minX), Float(blobRect.minY), Float(blobRect.width), Float(blobRect.height)) + + encoder.setVertexBytes(&rect, length: 4 * 4, index: 0) + encoder.setVertexBytes(&points, length: MemoryLayout.size * points.count, index: 1) + encoder.setVertexBytes(&count, length: MemoryLayout.size, index: 2) + + encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3 * 8 * points.count) + } + }) + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ContentOverlayButton.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ContentOverlayButton.swift new file mode 100644 index 00000000000..9347cbf35a1 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ContentOverlayButton.swift @@ -0,0 +1,207 @@ +import Foundation +import UIKit +import Display +import ComponentFlow + +final class ContentOverlayButton: HighlightTrackingButton, OverlayMaskContainerViewProtocol { + private struct ContentParams: Equatable { + var size: CGSize + var image: UIImage? + var isSelected: Bool + var isDestructive: Bool + + init(size: CGSize, image: UIImage?, isSelected: Bool, isDestructive: Bool) { + self.size = size + self.image = image + self.isSelected = isSelected + self.isDestructive = isDestructive + } + } + + let maskContents: UIView + + override static var layerClass: AnyClass { + return MirroringLayer.self + } + + var action: (() -> Void)? + + private let contentView: UIImageView + private var currentContentViewIsSelected: Bool? + + private let textView: TextView + + private var contentParams: ContentParams? + + override init(frame: CGRect) { + self.maskContents = UIView() + + self.contentView = UIImageView() + self.textView = TextView() + + super.init(frame: frame) + + self.addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside) + + let size: CGFloat = 56.0 + let renderer = UIGraphicsImageRenderer(bounds: CGRect(origin: CGPoint(), size: CGSize(width: size, height: size))) + self.maskContents.layer.contents = renderer.image { context in + UIGraphicsPushContext(context.cgContext) + context.cgContext.setFillColor(UIColor.white.cgColor) + context.cgContext.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: size, height: size))) + UIGraphicsPopContext() + }.cgImage + + (self.layer as? MirroringLayer)?.targetLayer = self.maskContents.layer + + self.addSubview(self.contentView) + self.addSubview(self.textView) + + self.internalHighligthedChanged = { [weak self] highlighted in + if let self, self.bounds.width > 0.0 { + let topScale: CGFloat = (self.bounds.width - 8.0) / self.bounds.width + let maxScale: CGFloat = (self.bounds.width + 2.0) / self.bounds.width + + if highlighted { + self.layer.removeAnimation(forKey: "opacity") + self.layer.removeAnimation(forKey: "sublayerTransform") + let transition = Transition(animation: .curve(duration: 0.15, curve: .easeInOut)) + transition.setScale(layer: self.layer, scale: topScale) + } else { + let t = self.layer.presentation()?.transform ?? layer.transform + let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13)) + + let transition = Transition(animation: .none) + transition.setScale(layer: self.layer, scale: 1.0) + + self.layer.animateScale(from: currentScale, to: maxScale, duration: 0.13, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, completion: { [weak self] completed in + guard let self, completed else { + return + } + + self.layer.animateScale(from: maxScale, to: 1.0, duration: 0.1, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue) + }) + } + } + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func buttonPressed() { + self.action?() + } + + func update(size: CGSize, image: UIImage?, isSelected: Bool, isDestructive: Bool, title: String, transition: Transition) { + let contentParams = ContentParams(size: size, image: image, isSelected: isSelected, isDestructive: isDestructive) + if self.contentParams != contentParams { + self.contentParams = contentParams + self.updateContent(contentParams: contentParams, transition: transition) + } + + transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: size)) + + let textSize = self.textView.update(string: title, fontSize: 13.0, fontWeight: 0.0, color: .white, constrainedWidth: 100.0, transition: .immediate) + self.textView.frame = CGRect(origin: CGPoint(x: floor((size.width - textSize.width) * 0.5), y: size.height + 4.0), size: textSize) + } + + private func updateContent(contentParams: ContentParams, transition: Transition) { + let image = generateImage(contentParams.size, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + if contentParams.isDestructive { + context.setFillColor(UIColor(rgb: 0xFF3B30).cgColor) + } else { + context.setFillColor(UIColor(white: 1.0, alpha: contentParams.isSelected ? 1.0 : 0.0).cgColor) + } + context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) + + if let image = contentParams.image, let cgImage = image.cgImage { + let imageSize = CGSize(width: image.size.width * 0.8, height: image.size.height * 0.8) + let imageFrame = CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) * 0.5), y: floor((size.height - imageSize.height) * 0.5)), size: imageSize) + + context.saveGState() + context.translateBy(x: imageFrame.midX, y: imageFrame.midY) + context.scaleBy(x: 1.0, y: -1.0) + context.translateBy(x: -imageFrame.midX, y: -imageFrame.midY) + + context.clip(to: imageFrame, mask: cgImage) + context.setBlendMode(contentParams.isSelected ? .copy : .normal) + context.setFillColor(contentParams.isSelected ? UIColor.clear.cgColor : UIColor(white: 1.0, alpha: 1.0).cgColor) + context.fill(imageFrame) + + context.resetClip() + context.restoreGState() + } + }) + + if !transition.animation.isImmediate, let currentContentViewIsSelected = self.currentContentViewIsSelected, currentContentViewIsSelected != contentParams.isSelected, let previousImage = self.contentView.image, let image { + self.contentView.layer.mask = nil + let _ = previousImage + let _ = image + let _ = currentContentViewIsSelected + + let previousContentView = UIImageView(image: previousImage) + previousContentView.frame = self.contentView.frame + self.addSubview(previousContentView) + + let animationDuration = 0.16 + let animationTimingFunction: String = CAMediaTimingFunctionName.linear.rawValue + + if contentParams.isSelected { + let maskLayer = CAShapeLayer() + maskLayer.frame = self.contentView.bounds + maskLayer.path = UIBezierPath(ovalIn: self.contentView.bounds).cgPath + maskLayer.strokeColor = UIColor.black.cgColor + maskLayer.fillColor = nil + maskLayer.lineWidth = 1.0 + self.contentView.layer.mask = maskLayer + maskLayer.animate(from: 0.0 as NSNumber, to: contentParams.size.width as NSNumber, keyPath: "lineWidth", timingFunction: animationTimingFunction, duration: animationDuration, removeOnCompletion: false, completion: { [weak self, weak maskLayer] _ in + guard let self, let maskLayer, self.contentView.layer.mask === maskLayer else { + return + } + self.contentView.layer.mask = nil + }) + + let previousMaskLayer = CAShapeLayer() + previousMaskLayer.frame = previousContentView.bounds + previousMaskLayer.path = UIBezierPath(ovalIn: previousContentView.bounds).cgPath + previousMaskLayer.strokeColor = nil + previousMaskLayer.fillColor = UIColor.black.cgColor + previousContentView.layer.mask = previousMaskLayer + previousMaskLayer.animate(from: 1.0 as NSNumber, to: 0.0001 as NSNumber, keyPath: "transform.scale", timingFunction: animationTimingFunction, duration: animationDuration, removeOnCompletion: false, completion: { [weak previousContentView] _ in + previousContentView?.removeFromSuperview() + }) + } else { + let maskLayer = CAShapeLayer() + maskLayer.frame = self.contentView.bounds + maskLayer.path = UIBezierPath(ovalIn: self.contentView.bounds).cgPath + maskLayer.strokeColor = nil + maskLayer.fillColor = UIColor.black.cgColor + self.contentView.layer.mask = maskLayer + maskLayer.animate(from: 0.0001 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", timingFunction: animationTimingFunction, duration: animationDuration, removeOnCompletion: false, completion: { [weak self, weak maskLayer] _ in + guard let self, let maskLayer, self.contentView.layer.mask === maskLayer else { + return + } + self.contentView.layer.mask = nil + }) + + let previousMaskLayer = CAShapeLayer() + previousMaskLayer.frame = previousContentView.bounds + previousMaskLayer.path = UIBezierPath(ovalIn: previousContentView.bounds).cgPath + previousMaskLayer.strokeColor = UIColor.black.cgColor + previousMaskLayer.fillColor = nil + previousMaskLayer.lineWidth = 1.0 + previousContentView.layer.mask = previousMaskLayer + previousMaskLayer.animate(from: contentParams.size.width as NSNumber, to: 0.0 as NSNumber, keyPath: "lineWidth", timingFunction: animationTimingFunction, duration: animationDuration, removeOnCompletion: false, completion: { [weak previousContentView] _ in + previousContentView?.removeFromSuperview() + }) + } + } + + self.contentView.image = image + self.currentContentViewIsSelected = contentParams.isSelected + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ContentView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ContentView.swift new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ContentView.swift @@ -0,0 +1 @@ + diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/KeyEmojiView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/KeyEmojiView.swift new file mode 100644 index 00000000000..0576e3b30e7 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/KeyEmojiView.swift @@ -0,0 +1,52 @@ +import Foundation +import UIKit +import Display + +final class KeyEmojiView: UIView { + private let emojiViews: [TextView] + + let size: CGSize + + init(emoji: [String]) { + self.emojiViews = emoji.map { emoji in + TextView() + } + + let itemSpacing: CGFloat = 3.0 + + var height: CGFloat = 0.0 + var nextX = 0.0 + for i in 0 ..< self.emojiViews.count { + if nextX != 0.0 { + nextX += itemSpacing + } + let emojiView = self.emojiViews[i] + let itemSize = emojiView.update(string: emoji[i], fontSize: 16.0, fontWeight: 0.0, color: .white, constrainedWidth: 100.0, transition: .immediate) + if height == 0.0 { + height = itemSize.height + } + emojiView.frame = CGRect(origin: CGPoint(x: nextX, y: 0.0), size: itemSize) + nextX += itemSize.width + } + + self.size = CGSize(width: nextX, height: height) + + super.init(frame: CGRect()) + + for emojiView in self.emojiViews { + self.addSubview(emojiView) + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func animateIn() { + for i in 0 ..< self.emojiViews.count { + let emojiView = self.emojiViews[i] + emojiView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + emojiView.layer.animatePosition(from: CGPoint(x: -CGFloat(self.emojiViews.count - 1 - i) * 30.0, y: 0.0), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + } + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/MinimizedVideoContainerView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/MinimizedVideoContainerView.swift new file mode 100644 index 00000000000..90b9434f431 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/MinimizedVideoContainerView.swift @@ -0,0 +1,144 @@ +import Foundation +import UIKit +import Display +import MetalEngine +import ComponentFlow + +final class MinimizedVideoContainerView: UIView { + private struct Params: Equatable { + var size: CGSize + var insets: UIEdgeInsets + + init(size: CGSize, insets: UIEdgeInsets) { + self.size = size + self.insets = insets + } + } + + private struct VideoMetrics: Equatable { + var resolution: CGSize + var rotationAngle: Float + var sourceId: Int + + init(resolution: CGSize, rotationAngle: Float, sourceId: Int) { + self.resolution = resolution + self.rotationAngle = rotationAngle + self.sourceId = sourceId + } + } + + private let videoLayer: PrivateCallVideoLayer + + private var params: Params? + private var videoMetrics: VideoMetrics? + private var appliedVideoMetrics: VideoMetrics? + + var video: VideoSource? { + didSet { + self.video?.updated = { [weak self] in + guard let self else { + return + } + var videoMetrics: VideoMetrics? + if let currentOutput = self.video?.currentOutput { + self.videoLayer.video = currentOutput + videoMetrics = VideoMetrics(resolution: CGSize(width: CGFloat(currentOutput.y.width), height: CGFloat(currentOutput.y.height)), rotationAngle: currentOutput.rotationAngle, sourceId: currentOutput.sourceId) + } else { + self.videoLayer.video = nil + } + self.videoLayer.setNeedsUpdate() + + if self.videoMetrics != videoMetrics { + self.videoMetrics = videoMetrics + self.update(transition: .easeInOut(duration: 0.2)) + } + } + var videoMetrics: VideoMetrics? + if let currentOutput = self.video?.currentOutput { + self.videoLayer.video = currentOutput + videoMetrics = VideoMetrics(resolution: CGSize(width: CGFloat(currentOutput.y.width), height: CGFloat(currentOutput.y.height)), rotationAngle: currentOutput.rotationAngle, sourceId: currentOutput.sourceId) + } else { + self.videoLayer.video = nil + } + self.videoLayer.setNeedsUpdate() + + if self.videoMetrics != videoMetrics { + self.videoMetrics = videoMetrics + self.update(transition: .easeInOut(duration: 0.2)) + } + } + } + + override init(frame: CGRect) { + self.videoLayer = PrivateCallVideoLayer() + self.videoLayer.masksToBounds = true + + super.init(frame: frame) + + self.layer.addSublayer(self.videoLayer) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + return nil + } + + private func update(transition: Transition) { + guard let params = self.params else { + return + } + self.update(params: params, transition: transition) + } + + func update(size: CGSize, insets: UIEdgeInsets, transition: Transition) { + let params = Params(size: size, insets: insets) + if self.params == params { + return + } + self.params = params + + self.update(params: params, transition: transition) + } + + private func update(params: Params, transition: Transition) { + guard let videoMetrics = self.videoMetrics else { + return + } + + var transition = transition + if self.appliedVideoMetrics == nil { + transition = .immediate + } + self.appliedVideoMetrics = videoMetrics + + var rotatedResolution = videoMetrics.resolution + var videoIsRotated = false + if videoMetrics.rotationAngle == Float.pi * 0.5 || videoMetrics.rotationAngle == Float.pi * 3.0 / 2.0 { + rotatedResolution = CGSize(width: rotatedResolution.height, height: rotatedResolution.width) + videoIsRotated = true + } + + let videoSize = rotatedResolution.aspectFitted(CGSize(width: 160.0, height: 160.0)) + + let videoResolution = rotatedResolution.aspectFittedOrSmaller(CGSize(width: 1280, height: 1280)).aspectFittedOrSmaller(CGSize(width: videoSize.width * 3.0, height: videoSize.height * 3.0)) + let rotatedVideoResolution = videoIsRotated ? CGSize(width: videoResolution.height, height: videoResolution.width) : videoResolution + + let rotatedVideoSize = videoIsRotated ? CGSize(width: videoSize.height, height: videoSize.width) : videoSize + let rotatedVideoFrame = CGRect(origin: CGPoint(x: params.size.width - params.insets.right - videoSize.width, y: params.size.height - params.insets.bottom - videoSize.height), size: videoSize) + + transition.setPosition(layer: self.videoLayer, position: rotatedVideoFrame.center) + transition.setBounds(layer: self.videoLayer, bounds: CGRect(origin: CGPoint(), size: rotatedVideoSize)) + transition.setPosition(layer: self.videoLayer.blurredLayer, position: rotatedVideoFrame.center) + transition.setBounds(layer: self.videoLayer.blurredLayer, bounds: CGRect(origin: CGPoint(), size: rotatedVideoSize)) + + transition.setTransform(layer: self.videoLayer, transform: CATransform3DMakeRotation(CGFloat(videoMetrics.rotationAngle), 0.0, 0.0, 1.0)) + transition.setTransform(layer: self.videoLayer.blurredLayer, transform: CATransform3DMakeRotation(CGFloat(videoMetrics.rotationAngle), 0.0, 0.0, 1.0)) + + transition.setCornerRadius(layer: self.videoLayer, cornerRadius: 10.0) + + self.videoLayer.renderSpec = RenderLayerSpec(size: RenderSize(width: Int(rotatedVideoResolution.width), height: Int(rotatedVideoResolution.height))) + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/PrivateCallVideoLayer.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/PrivateCallVideoLayer.swift new file mode 100644 index 00000000000..e9098f3032f --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/PrivateCallVideoLayer.swift @@ -0,0 +1,258 @@ +import Foundation +import UIKit +import MetalKit +import MetalPerformanceShaders +import Accelerate +import MetalEngine + +func imageToCVPixelBuffer(image: UIImage) -> CVPixelBuffer? { + guard let cgImage = image.cgImage, let data = cgImage.dataProvider?.data, let bytes = CFDataGetBytePtr(data), let colorSpace = cgImage.colorSpace, case .rgb = colorSpace.model, cgImage.bitsPerPixel / cgImage.bitsPerComponent == 4 else { + return nil + } + + let width = cgImage.width + let height = cgImage.width + + var pixelBuffer: CVPixelBuffer? = nil + let _ = CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_32BGRA, [ + kCVPixelBufferIOSurfacePropertiesKey: NSDictionary() + ] as CFDictionary, &pixelBuffer) + guard let pixelBuffer else { + return nil + } + + CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0)) + defer { + CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0)) + } + guard let baseAddress = CVPixelBufferGetBaseAddress(pixelBuffer) else { + return nil + } + + var srcBuffer = vImage_Buffer(data: UnsafeMutableRawPointer(mutating: bytes), height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: cgImage.bytesPerRow) + var dstBuffer = vImage_Buffer(data: UnsafeMutableRawPointer(mutating: baseAddress), height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: CVPixelBufferGetBytesPerRow(pixelBuffer)) + + vImageCopyBuffer(&srcBuffer, &dstBuffer, 4, vImage_Flags(kvImageDoNotTile)) + + return pixelBuffer +} + +final class PrivateCallVideoLayer: MetalEngineSubjectLayer, MetalEngineSubject { + var internalData: MetalEngineSubjectInternalData? + + let blurredLayer: MetalEngineSubjectLayer + + final class BlurState: ComputeState { + let computePipelineStateYUVToRGBA: MTLComputePipelineState + let computePipelineStateHorizontal: MTLComputePipelineState + let computePipelineStateVertical: MTLComputePipelineState + let downscaleKernel: MPSImageBilinearScale + + required init?(device: MTLDevice) { + guard let library = metalLibrary(device: device) else { + return nil + } + guard let functionVideoYUVToRGBA = library.makeFunction(name: "videoYUVToRGBA") else { + return nil + } + guard let computePipelineStateYUVToRGBA = try? device.makeComputePipelineState(function: functionVideoYUVToRGBA) else { + return nil + } + self.computePipelineStateYUVToRGBA = computePipelineStateYUVToRGBA + + guard let gaussianBlurHorizontal = library.makeFunction(name: "gaussianBlurHorizontal"), let gaussianBlurVertical = library.makeFunction(name: "gaussianBlurVertical") else { + return nil + } + guard let computePipelineStateHorizontal = try? device.makeComputePipelineState(function: gaussianBlurHorizontal) else { + return nil + } + self.computePipelineStateHorizontal = computePipelineStateHorizontal + + guard let computePipelineStateVertical = try? device.makeComputePipelineState(function: gaussianBlurVertical) else { + return nil + } + self.computePipelineStateVertical = computePipelineStateVertical + + self.downscaleKernel = MPSImageBilinearScale(device: device) + } + } + + final class RenderState: RenderToLayerState { + let pipelineState: MTLRenderPipelineState + + required init?(device: MTLDevice) { + guard let library = metalLibrary(device: device) else { + return nil + } + guard let vertexFunction = library.makeFunction(name: "mainVideoVertex"), let fragmentFunction = library.makeFunction(name: "mainVideoFragment") else { + return nil + } + + let pipelineDescriptor = MTLRenderPipelineDescriptor() + pipelineDescriptor.vertexFunction = vertexFunction + pipelineDescriptor.fragmentFunction = fragmentFunction + pipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm + guard let pipelineState = try? device.makeRenderPipelineState(descriptor: pipelineDescriptor) else { + return nil + } + self.pipelineState = pipelineState + } + } + + var video: VideoSource.Output? { + didSet { + self.setNeedsUpdate() + } + } + + var renderSpec: RenderLayerSpec? + + private var rgbaTexture: PooledTexture? + private var downscaledTexture: PooledTexture? + private var blurredHorizontalTexture: PooledTexture? + private var blurredVerticalTexture: PooledTexture? + + override init() { + self.blurredLayer = MetalEngineSubjectLayer() + + super.init() + } + + override init(layer: Any) { + self.blurredLayer = MetalEngineSubjectLayer() + + super.init(layer: layer) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(context: MetalEngineSubjectContext) { + if self.isHidden { + return + } + guard let renderSpec = self.renderSpec else { + return + } + guard let videoTextures = self.video else { + return + } + + let rgbaTextureSpec = TextureSpec(width: videoTextures.y.width, height: videoTextures.y.height, pixelFormat: .rgba8UnsignedNormalized) + if self.rgbaTexture == nil || self.rgbaTexture?.spec != rgbaTextureSpec { + self.rgbaTexture = MetalEngine.shared.pooledTexture(spec: rgbaTextureSpec) + } + if self.downscaledTexture == nil { + self.downscaledTexture = MetalEngine.shared.pooledTexture(spec: TextureSpec(width: 128, height: 128, pixelFormat: .rgba8UnsignedNormalized)) + } + if self.blurredHorizontalTexture == nil { + self.blurredHorizontalTexture = MetalEngine.shared.pooledTexture(spec: TextureSpec(width: 128, height: 128, pixelFormat: .rgba8UnsignedNormalized)) + } + if self.blurredVerticalTexture == nil { + self.blurredVerticalTexture = MetalEngine.shared.pooledTexture(spec: TextureSpec(width: 128, height: 128, pixelFormat: .rgba8UnsignedNormalized)) + } + + guard let rgbaTexture = self.rgbaTexture?.get(context: context) else { + return + } + + let _ = context.compute(state: BlurState.self, inputs: rgbaTexture.placeholer, commands: { commandBuffer, blurState, rgbaTexture in + guard let rgbaTexture else { + return + } + guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { + return + } + + let threadgroupSize = MTLSize(width: 16, height: 16, depth: 1) + let threadgroupCount = MTLSize(width: (rgbaTexture.width + threadgroupSize.width - 1) / threadgroupSize.width, height: (rgbaTexture.height + threadgroupSize.height - 1) / threadgroupSize.height, depth: 1) + + computeEncoder.setComputePipelineState(blurState.computePipelineStateYUVToRGBA) + computeEncoder.setTexture(videoTextures.y, index: 0) + computeEncoder.setTexture(videoTextures.uv, index: 1) + computeEncoder.setTexture(rgbaTexture, index: 2) + computeEncoder.dispatchThreadgroups(threadgroupCount, threadsPerThreadgroup: threadgroupSize) + + computeEncoder.endEncoding() + }) + + if !self.blurredLayer.isHidden { + guard let downscaledTexture = self.downscaledTexture?.get(context: context), let blurredHorizontalTexture = self.blurredHorizontalTexture?.get(context: context), let blurredVerticalTexture = self.blurredVerticalTexture?.get(context: context) else { + return + } + + let blurredTexture = context.compute(state: BlurState.self, inputs: rgbaTexture.placeholer, downscaledTexture.placeholer, blurredHorizontalTexture.placeholer, blurredVerticalTexture.placeholer, commands: { commandBuffer, blurState, rgbaTexture, downscaledTexture, blurredHorizontalTexture, blurredVerticalTexture -> MTLTexture? in + guard let rgbaTexture, let downscaledTexture, let blurredHorizontalTexture, let blurredVerticalTexture else { + return nil + } + + blurState.downscaleKernel.encode(commandBuffer: commandBuffer, sourceTexture: rgbaTexture, destinationTexture: downscaledTexture) + + do { + guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { + return nil + } + + let threadgroupSize = MTLSize(width: 16, height: 16, depth: 1) + let threadgroupCount = MTLSize(width: (downscaledTexture.width + threadgroupSize.width - 1) / threadgroupSize.width, height: (downscaledTexture.height + threadgroupSize.height - 1) / threadgroupSize.height, depth: 1) + + computeEncoder.setComputePipelineState(blurState.computePipelineStateHorizontal) + computeEncoder.setTexture(downscaledTexture, index: 0) + computeEncoder.setTexture(blurredHorizontalTexture, index: 1) + computeEncoder.dispatchThreadgroups(threadgroupCount, threadsPerThreadgroup: threadgroupSize) + + computeEncoder.setComputePipelineState(blurState.computePipelineStateVertical) + computeEncoder.setTexture(blurredHorizontalTexture, index: 0) + computeEncoder.setTexture(blurredVerticalTexture, index: 1) + computeEncoder.dispatchThreadgroups(threadgroupCount, threadsPerThreadgroup: threadgroupSize) + + computeEncoder.endEncoding() + } + + return blurredVerticalTexture + }) + + context.renderToLayer(spec: renderSpec, state: RenderState.self, layer: self.blurredLayer, inputs: blurredTexture, commands: { encoder, placement, blurredTexture in + guard let blurredTexture else { + return + } + let effectiveRect = placement.effectiveRect + + var rect = SIMD4(Float(effectiveRect.minX), Float(effectiveRect.minY), Float(effectiveRect.width), Float(effectiveRect.height)) + encoder.setVertexBytes(&rect, length: 4 * 4, index: 0) + encoder.setFragmentTexture(blurredTexture, index: 0) + + var brightness: Float = 1.0 + var saturation: Float = 1.2 + var overlay: SIMD4 = SIMD4(1.0, 1.0, 1.0, 0.2) + encoder.setFragmentBytes(&brightness, length: 4, index: 0) + encoder.setFragmentBytes(&saturation, length: 4, index: 1) + encoder.setFragmentBytes(&overlay, length: 4 * 4, index: 2) + + encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6) + }) + } + + context.renderToLayer(spec: renderSpec, state: RenderState.self, layer: self, inputs: rgbaTexture.placeholer, commands: { encoder, placement, rgbaTexture in + guard let rgbaTexture else { + return + } + + let effectiveRect = placement.effectiveRect + + var rect = SIMD4(Float(effectiveRect.minX), Float(effectiveRect.minY), Float(effectiveRect.width), Float(effectiveRect.height)) + encoder.setVertexBytes(&rect, length: 4 * 4, index: 0) + encoder.setFragmentTexture(rgbaTexture, index: 0) + + var brightness: Float = 1.0 + var saturation: Float = 1.0 + var overlay: SIMD4 = SIMD4() + encoder.setFragmentBytes(&brightness, length: 4, index: 0) + encoder.setFragmentBytes(&saturation, length: 4, index: 1) + encoder.setFragmentBytes(&overlay, length: 4 * 4, index: 2) + + encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6) + }) + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/StatusView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/StatusView.swift new file mode 100644 index 00000000000..2c798396274 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/StatusView.swift @@ -0,0 +1,377 @@ +import Foundation +import UIKit +import Display +import ComponentFlow + +private func addRoundedRectPath(context: CGContext, rect: CGRect, radius: CGFloat) { + context.saveGState() + context.translateBy(x: rect.minX, y: rect.minY) + context.scaleBy(x: radius, y: radius) + let fw = rect.width / radius + let fh = rect.height / radius + context.move(to: CGPoint(x: fw, y: fh / 2.0)) + context.addArc(tangent1End: CGPoint(x: fw, y: fh), tangent2End: CGPoint(x: fw/2, y: fh), radius: 1.0) + context.addArc(tangent1End: CGPoint(x: 0, y: fh), tangent2End: CGPoint(x: 0, y: fh/2), radius: 1) + context.addArc(tangent1End: CGPoint(x: 0, y: 0), tangent2End: CGPoint(x: fw/2, y: 0), radius: 1) + context.addArc(tangent1End: CGPoint(x: fw, y: 0), tangent2End: CGPoint(x: fw, y: fh/2), radius: 1) + context.closePath() + context.restoreGState() +} + +private func stringForDuration(_ duration: Int) -> String { + let hours = duration / 3600 + let minutes = duration / 60 % 60 + let seconds = duration % 60 + let durationString: String + if hours > 0 { + durationString = String(format: "%d:%02d:%02d", hours, minutes, seconds) + } else { + durationString = String(format: "%d:%02d", minutes, seconds) + } + return durationString +} + + +private final class AnimatedDotsLayer: SimpleLayer { + private let dotLayers: [SimpleLayer] + + let size: CGSize + + override init() { + self.dotLayers = (0 ..< 3).map { _ in + SimpleLayer() + } + + let dotSpacing: CGFloat = 1.0 + let dotSize = CGSize(width: 5.0, height: 5.0) + + self.size = CGSize(width: CGFloat(self.dotLayers.count) * dotSize.width + CGFloat(self.dotLayers.count - 1) * dotSpacing, height: dotSize.height) + + super.init() + + let dotImage = UIGraphicsImageRenderer(size: dotSize).image(actions: { context in + context.cgContext.setFillColor(UIColor.white.cgColor) + context.cgContext.fillEllipse(in: CGRect(origin: CGPoint(), size: dotSize)) + }) + + var nextX: CGFloat = 0.0 + for dotLayer in self.dotLayers { + dotLayer.contents = dotImage.cgImage + dotLayer.frame = CGRect(origin: CGPoint(x: nextX, y: 0.0), size: dotSize) + nextX += dotSpacing + dotSize.width + self.addSublayer(dotLayer) + } + + self.didEnterHierarchy = { [weak self] in + self?.updateAnimations() + } + } + + override init(layer: Any) { + self.dotLayers = [] + self.size = CGSize() + + super.init(layer: layer) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func updateAnimations() { + if self.dotLayers[0].animation(forKey: "dotAnimation") != nil { + return + } + + let animationDuration: Double = 0.6 + for i in 0 ..< self.dotLayers.count { + let dotLayer = self.dotLayers[i] + + let animation = CABasicAnimation(keyPath: "transform.scale") + animation.duration = animationDuration + animation.fromValue = 0.3 + animation.toValue = 1.0 + animation.timingFunction = CAMediaTimingFunction(name: .linear) + animation.autoreverses = true + animation.repeatCount = .infinity + animation.timeOffset = CGFloat(self.dotLayers.count - 1 - i) * animationDuration * 0.33 + + dotLayer.add(animation, forKey: "dotAnimation") + } + } +} + +private final class SignalStrengthView: UIView { + let barViews: [UIImageView] + + let size: CGSize + + override init(frame: CGRect) { + self.barViews = (0 ..< 4).map { _ in + return UIImageView() + } + + let itemWidth: CGFloat = 3.0 + let itemHeight: CGFloat = 12.0 + let itemSpacing: CGFloat = 2.0 + + self.size = CGSize(width: CGFloat(self.barViews.count) * itemWidth + CGFloat(self.barViews.count - 1) * itemSpacing, height: itemHeight) + + super.init(frame: frame) + + let itemImage = UIGraphicsImageRenderer(size: CGSize(width: itemWidth, height: itemWidth)).image(actions: { context in + context.cgContext.setFillColor(UIColor.white.cgColor) + addRoundedRectPath(context: context.cgContext, rect: CGRect(origin: CGPoint(), size: CGSize(width: itemWidth, height: itemWidth)), radius: 1.0) + context.cgContext.fillPath() + }).stretchableImage(withLeftCapWidth: Int(itemWidth * 0.5), topCapHeight: Int(itemWidth * 0.5)) + + var nextX: CGFloat = 0.0 + + for i in 0 ..< self.barViews.count { + let barView = self.barViews[i] + barView.image = itemImage + let barHeight = floor(CGFloat(i + 1) * itemHeight / CGFloat(self.barViews.count)) + barView.frame = CGRect(origin: CGPoint(x: nextX, y: itemHeight - barHeight), size: CGSize(width: itemWidth, height: barHeight)) + nextX += itemSpacing + itemWidth + self.addSubview(barView) + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(value: Double) { + for i in 0 ..< self.barViews.count { + if value >= Double(i + 1) / Double(self.barViews.count) { + self.barViews[i].alpha = 1.0 + } else { + self.barViews[i].alpha = 0.5 + } + } + } +} + +final class StatusView: UIView { + private struct LayoutState: Equatable { + var state: State + var size: CGSize + + init(state: State, size: CGSize) { + self.state = state + self.size = size + } + } + + enum WaitingState { + case requesting + case ringing + case generatingKeys + } + + struct ActiveState: Equatable { + var startTimestamp: Double + var signalStrength: Double + + init(startTimestamp: Double, signalStrength: Double) { + self.startTimestamp = startTimestamp + self.signalStrength = signalStrength + } + } + + struct TerminatedState: Equatable { + var duration: Double + + init(duration: Double) { + self.duration = duration + } + } + + enum State: Equatable { + enum Key: Equatable { + case waiting(WaitingState) + case active + case terminated + } + + case waiting(WaitingState) + case active(ActiveState) + case terminated(TerminatedState) + + var key: Key { + switch self { + case let .waiting(waitingState): + return .waiting(waitingState) + case .active: + return .active + case .terminated: + return .terminated + } + } + } + + private let textView: TextView + + private var dotsLayer: AnimatedDotsLayer? + private var signalStrengthView: SignalStrengthView? + + private var activeDurationTimer: Foundation.Timer? + + private var layoutState: LayoutState? + var state: State? { + return self.layoutState?.state + } + + var requestLayout: (() -> Void)? + + override init(frame: CGRect) { + self.textView = TextView() + + super.init(frame: frame) + + self.addSubview(self.textView) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.activeDurationTimer?.invalidate() + } + + func update(state: State, transition: Transition) -> CGSize { + if let layoutState = self.layoutState, layoutState.state == state { + return layoutState.size + } + let size = self.updateInternal(state: state, transition: transition) + self.layoutState = LayoutState(state: state, size: size) + + self.updateActiveDurationTimer() + + return size + } + + private func updateActiveDurationTimer() { + if let layoutState = self.layoutState, case let .active(activeState) = layoutState.state { + if self.activeDurationTimer == nil { + let timestamp = Date().timeIntervalSince1970 + let duration = timestamp - activeState.startTimestamp + let nextTickDelay = ceil(duration) - duration + 0.05 + + self.activeDurationTimer = Foundation.Timer.scheduledTimer(withTimeInterval: nextTickDelay, repeats: false, block: { [weak self] _ in + guard let self else { + return + } + self.activeDurationTimer?.invalidate() + self.activeDurationTimer = nil + + if let layoutState = self.layoutState { + let size = self.updateInternal(state: layoutState.state, transition: .immediate) + if layoutState.size != size { + self.layoutState = nil + self.requestLayout?() + } + } + + self.updateActiveDurationTimer() + }) + } + } else { + if let activeDurationTimer = self.activeDurationTimer { + self.activeDurationTimer = nil + activeDurationTimer.invalidate() + } + } + } + + private func updateInternal(state: State, transition: Transition) -> CGSize { + let textString: String + var needsDots = false + var monospacedDigits = false + var signalStrength: Double? + switch state { + case let .waiting(waitingState): + needsDots = true + + switch waitingState { + case .requesting: + textString = "Requesting" + case .ringing: + textString = "Ringing" + case .generatingKeys: + textString = "Exchanging encryption keys" + } + case let .active(activeState): + monospacedDigits = true + + let timestamp = Date().timeIntervalSince1970 + let duration = timestamp - activeState.startTimestamp + textString = stringForDuration(Int(duration)) + signalStrength = activeState.signalStrength + case let .terminated(terminatedState): + textString = stringForDuration(Int(terminatedState.duration)) + } + + var contentSize = CGSize() + + if let signalStrength { + let signalStrengthView: SignalStrengthView + if let current = self.signalStrengthView { + signalStrengthView = current + } else { + signalStrengthView = SignalStrengthView(frame: CGRect()) + self.signalStrengthView = signalStrengthView + self.addSubview(signalStrengthView) + } + signalStrengthView.update(value: signalStrength) + contentSize.width += signalStrengthView.size.width + 7.0 + } else { + if let signalStrengthView = self.signalStrengthView { + self.signalStrengthView = nil + signalStrengthView.removeFromSuperview() + } + } + + let textSize = self.textView.update(string: textString, fontSize: 16.0, fontWeight: 0.0, monospacedDigits: monospacedDigits, color: .white, constrainedWidth: 250.0, transition: .immediate) + let textFrame = CGRect(origin: CGPoint(x: contentSize.width, y: 0.0), size: textSize) + if self.textView.bounds.isEmpty { + self.textView.frame = textFrame + } else { + transition.setPosition(view: self.textView, position: textFrame.center) + transition.setBounds(view: self.textView, bounds: CGRect(origin: CGPoint(), size: textFrame.size)) + } + + contentSize.width += textSize.width + contentSize.height = textSize.height + + if let signalStrengthView = self.signalStrengthView { + transition.setFrame(view: signalStrengthView, frame: CGRect(origin: CGPoint(x: 0.0, y: floor((textSize.height - signalStrengthView.size.height) * 0.5)), size: signalStrengthView.size)) + } + + if needsDots { + let dotsLayer: AnimatedDotsLayer + if let current = self.dotsLayer { + dotsLayer = current + } else { + dotsLayer = AnimatedDotsLayer() + self.dotsLayer = dotsLayer + self.layer.addSublayer(dotsLayer) + transition.animateAlpha(layer: dotsLayer, from: 0.0, to: 1.0) + } + + let dotsSpacing: CGFloat = 6.0 + + let dotsFrame = CGRect(origin: CGPoint(x: textSize.width + dotsSpacing, y: 1.0 + floor((textSize.height - dotsLayer.size.height) * 0.5)), size: dotsLayer.size) + transition.setFrame(layer: dotsLayer, frame: dotsFrame) + contentSize.width += dotsSpacing + dotsFrame.width + } else if let dotsLayer = self.dotsLayer { + self.dotsLayer = nil + transition.setAlpha(layer: dotsLayer, alpha: 0.0, completion: { [weak dotsLayer] _ in + dotsLayer?.removeFromSuperlayer() + }) + } + + return contentSize + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/TitleView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/TitleView.swift new file mode 100644 index 00000000000..0b5e4aad967 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/TitleView.swift @@ -0,0 +1,84 @@ +import Foundation +import UIKit +import ComponentFlow + +final class TextView: UIView { + private struct Params: Equatable { + var string: String + var fontSize: CGFloat + var fontWeight: CGFloat + var monospacedDigits: Bool + var constrainedWidth: CGFloat + } + + private struct LayoutState: Equatable { + var params: Params + var size: CGSize + var attributedString: NSAttributedString + } + + private var layoutState: LayoutState? + private var animateContentsTransition: Bool = false + + override init(frame: CGRect) { + super.init(frame: CGRect()) + + self.isOpaque = false + self.backgroundColor = nil + self.contentMode = .center + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func action(for layer: CALayer, forKey event: String) -> CAAction? { + if self.animateContentsTransition && event == "contents" { + self.animateContentsTransition = false + let animation = CABasicAnimation(keyPath: "contents") + animation.duration = 0.15 * UIView.animationDurationFactor() + animation.timingFunction = CAMediaTimingFunction(name: .linear) + return animation + } + return super.action(for: layer, forKey: event) + } + + func update(string: String, fontSize: CGFloat, fontWeight: CGFloat, monospacedDigits: Bool = false, color: UIColor, constrainedWidth: CGFloat, transition: Transition) -> CGSize { + let params = Params(string: string, fontSize: fontSize, fontWeight: fontWeight, monospacedDigits: monospacedDigits, constrainedWidth: constrainedWidth) + if let layoutState = self.layoutState, layoutState.params == params { + return layoutState.size + } + + let font: UIFont + if monospacedDigits { + font = UIFont.monospacedDigitSystemFont(ofSize: fontSize, weight: UIFont.Weight(fontWeight)) + } else { + font = UIFont.systemFont(ofSize: fontSize, weight: UIFont.Weight(fontWeight)) + } + + let attributedString = NSAttributedString(string: string, attributes: [ + .font: font, + .foregroundColor: color, + ]) + let stringBounds = attributedString.boundingRect(with: CGSize(width: constrainedWidth, height: 200.0), options: .usesLineFragmentOrigin, context: nil) + let stringSize = CGSize(width: ceil(stringBounds.width), height: ceil(stringBounds.height)) + let size = CGSize(width: min(constrainedWidth, stringSize.width), height: stringSize.height) + + let layoutState = LayoutState(params: params, size: size, attributedString: attributedString) + if self.layoutState != layoutState { + self.layoutState = layoutState + self.animateContentsTransition = !transition.animation.isImmediate + self.setNeedsDisplay() + } + + return size + } + + override func draw(_ rect: CGRect) { + guard let layoutState = self.layoutState else { + return + } + + layoutState.attributedString.draw(with: rect, options: [.truncatesLastVisibleLine, .usesLineFragmentOrigin], context: nil) + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/VideoContainerView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/VideoContainerView.swift new file mode 100644 index 00000000000..022fae6ebbf --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/VideoContainerView.swift @@ -0,0 +1,236 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import MetalEngine + +private let shadowImage: UIImage? = { + UIImage(named: "Call/VideoGradient")?.precomposed() +}() + +final class VideoContainerView: UIView { + private struct Params: Equatable { + var size: CGSize + var insets: UIEdgeInsets + var cornerRadius: CGFloat + var isMinimized: Bool + var isAnimatingOut: Bool + + init(size: CGSize, insets: UIEdgeInsets, cornerRadius: CGFloat, isMinimized: Bool, isAnimatingOut: Bool) { + self.size = size + self.insets = insets + self.cornerRadius = cornerRadius + self.isMinimized = isMinimized + self.isAnimatingOut = isAnimatingOut + } + } + + private struct VideoMetrics: Equatable { + var resolution: CGSize + var rotationAngle: Float + + init(resolution: CGSize, rotationAngle: Float) { + self.resolution = resolution + self.rotationAngle = rotationAngle + } + } + + private let videoLayer: PrivateCallVideoLayer + let blurredContainerLayer: SimpleLayer + + private let topShadowView: UIImageView + private let bottomShadowView: UIImageView + + private var params: Params? + private var videoMetrics: VideoMetrics? + private var appliedVideoMetrics: VideoMetrics? + + var video: VideoSource? { + didSet { + self.video?.updated = { [weak self] in + guard let self else { + return + } + var videoMetrics: VideoMetrics? + if let currentOutput = self.video?.currentOutput { + self.videoLayer.video = currentOutput + videoMetrics = VideoMetrics(resolution: CGSize(width: CGFloat(currentOutput.y.width), height: CGFloat(currentOutput.y.height)), rotationAngle: currentOutput.rotationAngle) + } else { + self.videoLayer.video = nil + } + self.videoLayer.setNeedsUpdate() + + if self.videoMetrics != videoMetrics { + self.videoMetrics = videoMetrics + self.update(transition: .easeInOut(duration: 0.2)) + } + } + var videoMetrics: VideoMetrics? + if let currentOutput = self.video?.currentOutput { + self.videoLayer.video = currentOutput + videoMetrics = VideoMetrics(resolution: CGSize(width: CGFloat(currentOutput.y.width), height: CGFloat(currentOutput.y.height)), rotationAngle: currentOutput.rotationAngle) + } else { + self.videoLayer.video = nil + } + self.videoLayer.setNeedsUpdate() + + if self.videoMetrics != videoMetrics { + self.videoMetrics = videoMetrics + self.update(transition: .easeInOut(duration: 0.2)) + } + } + } + + override init(frame: CGRect) { + self.videoLayer = PrivateCallVideoLayer() + self.blurredContainerLayer = SimpleLayer() + + self.topShadowView = UIImageView() + self.topShadowView.transform = CGAffineTransformMakeScale(1.0, -1.0) + self.bottomShadowView = UIImageView() + + super.init(frame: frame) + + self.backgroundColor = UIColor.black + self.blurredContainerLayer.backgroundColor = UIColor.black.cgColor + + self.layer.addSublayer(self.videoLayer) + self.blurredContainerLayer.addSublayer(self.videoLayer.blurredLayer) + + self.topShadowView.image = shadowImage + self.bottomShadowView.image = shadowImage + self.addSubview(self.topShadowView) + self.addSubview(self.bottomShadowView) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func update(transition: Transition) { + guard let params = self.params else { + return + } + self.update(params: params, transition: transition) + } + + func update(size: CGSize, insets: UIEdgeInsets, cornerRadius: CGFloat, isMinimized: Bool, isAnimatingOut: Bool, transition: Transition) { + let params = Params(size: size, insets: insets, cornerRadius: cornerRadius, isMinimized: isMinimized, isAnimatingOut: isAnimatingOut) + if self.params == params { + return + } + + self.layer.masksToBounds = true + if self.layer.animation(forKey: "cornerRadius") == nil { + self.layer.cornerRadius = self.params?.cornerRadius ?? 0.0 + } + + self.params = params + + transition.setCornerRadius(layer: self.layer, cornerRadius: params.cornerRadius, completion: { [weak self] completed in + guard let self, let params = self.params, completed else { + return + } + if !params.isAnimatingOut { + self.layer.masksToBounds = false + self.layer.cornerRadius = 0.0 + } + }) + + self.update(params: params, transition: transition) + } + + private func update(params: Params, transition: Transition) { + guard let videoMetrics = self.videoMetrics else { + return + } + var transition = transition + if self.appliedVideoMetrics == nil { + transition = .immediate + } + self.appliedVideoMetrics = videoMetrics + + if params.isMinimized { + var rotatedResolution = videoMetrics.resolution + var videoIsRotated = false + if videoMetrics.rotationAngle == Float.pi * 0.5 || videoMetrics.rotationAngle == Float.pi * 3.0 / 2.0 { + rotatedResolution = CGSize(width: rotatedResolution.height, height: rotatedResolution.width) + videoIsRotated = true + } + + let videoSize = rotatedResolution.aspectFitted(CGSize(width: 160.0, height: 160.0)) + + let videoResolution = rotatedResolution.aspectFittedOrSmaller(CGSize(width: 1280, height: 1280)).aspectFittedOrSmaller(CGSize(width: videoSize.width * 3.0, height: videoSize.height * 3.0)) + let rotatedVideoResolution = videoIsRotated ? CGSize(width: videoResolution.height, height: videoResolution.width) : videoResolution + + let rotatedVideoSize = videoIsRotated ? CGSize(width: videoSize.height, height: videoSize.width) : videoSize + let rotatedVideoFrame = CGRect(origin: CGPoint(x: params.size.width - params.insets.right - videoSize.width, y: params.size.height - params.insets.bottom - videoSize.height), size: videoSize) + let effectiveVideoFrame = videoSize.centered(around: rotatedVideoFrame.center) + + transition.setPosition(layer: self.videoLayer, position: rotatedVideoFrame.center) + transition.setBounds(layer: self.videoLayer, bounds: CGRect(origin: CGPoint(), size: rotatedVideoSize)) + transition.setPosition(layer: self.videoLayer.blurredLayer, position: rotatedVideoFrame.center) + transition.setBounds(layer: self.videoLayer.blurredLayer, bounds: CGRect(origin: CGPoint(), size: rotatedVideoSize)) + + transition.setTransform(layer: self.videoLayer, transform: CATransform3DMakeRotation(CGFloat(videoMetrics.rotationAngle), 0.0, 0.0, 1.0)) + transition.setTransform(layer: self.videoLayer.blurredLayer, transform: CATransform3DMakeRotation(CGFloat(videoMetrics.rotationAngle), 0.0, 0.0, 1.0)) + + transition.setCornerRadius(layer: self.videoLayer, cornerRadius: 10.0) + + self.videoLayer.renderSpec = RenderLayerSpec(size: RenderSize(width: Int(rotatedVideoResolution.width), height: Int(rotatedVideoResolution.height))) + + let topShadowHeight: CGFloat = floor(effectiveVideoFrame.height * 0.2) + let topShadowFrame = CGRect(origin: effectiveVideoFrame.origin, size: CGSize(width: effectiveVideoFrame.width, height: topShadowHeight)) + transition.setPosition(view: self.topShadowView, position: topShadowFrame.center) + transition.setBounds(view: self.topShadowView, bounds: CGRect(origin: CGPoint(x: effectiveVideoFrame.minX, y: effectiveVideoFrame.maxY - topShadowHeight), size: topShadowFrame.size)) + transition.setAlpha(view: self.topShadowView, alpha: 0.0) + + let bottomShadowHeight: CGFloat = 200.0 + transition.setFrame(view: self.bottomShadowView, frame: CGRect(origin: CGPoint(x: 0.0, y: params.size.height - bottomShadowHeight), size: CGSize(width: params.size.width, height: bottomShadowHeight))) + transition.setAlpha(view: self.bottomShadowView, alpha: 0.0) + } else { + var rotatedResolution = videoMetrics.resolution + var videoIsRotated = false + if videoMetrics.rotationAngle == Float.pi * 0.5 || videoMetrics.rotationAngle == Float.pi * 3.0 / 2.0 { + rotatedResolution = CGSize(width: rotatedResolution.height, height: rotatedResolution.width) + videoIsRotated = true + } + + var videoSize = rotatedResolution.aspectFitted(params.size) + let boundingAspectRatio = params.size.width / params.size.height + let videoAspectRatio = videoSize.width / videoSize.height + if abs(boundingAspectRatio - videoAspectRatio) < 0.15 { + videoSize = rotatedResolution.aspectFilled(params.size) + } + + let videoResolution = rotatedResolution.aspectFittedOrSmaller(CGSize(width: 1280, height: 1280)).aspectFittedOrSmaller(CGSize(width: videoSize.width * 3.0, height: videoSize.height * 3.0)) + let rotatedVideoResolution = videoIsRotated ? CGSize(width: videoResolution.height, height: videoResolution.width) : videoResolution + + let rotatedVideoSize = videoIsRotated ? CGSize(width: videoSize.height, height: videoSize.width) : videoSize + let rotatedBoundingSize = params.size + let rotatedVideoFrame = CGRect(origin: CGPoint(x: floor((rotatedBoundingSize.width - rotatedVideoSize.width) * 0.5), y: floor((rotatedBoundingSize.height - rotatedVideoSize.height) * 0.5)), size: rotatedVideoSize) + + transition.setPosition(layer: self.videoLayer, position: rotatedVideoFrame.center) + transition.setBounds(layer: self.videoLayer, bounds: CGRect(origin: CGPoint(), size: rotatedVideoFrame.size)) + transition.setPosition(layer: self.videoLayer.blurredLayer, position: rotatedVideoFrame.center) + transition.setBounds(layer: self.videoLayer.blurredLayer, bounds: CGRect(origin: CGPoint(), size: rotatedVideoFrame.size)) + + transition.setTransform(layer: self.videoLayer, transform: CATransform3DMakeRotation(CGFloat(videoMetrics.rotationAngle), 0.0, 0.0, 1.0)) + transition.setTransform(layer: self.videoLayer.blurredLayer, transform: CATransform3DMakeRotation(CGFloat(videoMetrics.rotationAngle), 0.0, 0.0, 1.0)) + + if !params.isAnimatingOut { + self.videoLayer.renderSpec = RenderLayerSpec(size: RenderSize(width: Int(rotatedVideoResolution.width), height: Int(rotatedVideoResolution.height))) + } + + let topShadowHeight: CGFloat = 200.0 + let topShadowFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.size.width, height: topShadowHeight)) + transition.setPosition(view: self.topShadowView, position: topShadowFrame.center) + transition.setBounds(view: self.topShadowView, bounds: CGRect(origin: CGPoint(), size: topShadowFrame.size)) + transition.setAlpha(view: self.topShadowView, alpha: 1.0) + + let bottomShadowHeight: CGFloat = 200.0 + transition.setFrame(view: self.bottomShadowView, frame: CGRect(origin: CGPoint(x: 0.0, y: params.size.height - bottomShadowHeight), size: CGSize(width: params.size.width, height: bottomShadowHeight))) + transition.setAlpha(view: self.bottomShadowView, alpha: 1.0) + } + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/WeakSignalView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/WeakSignalView.swift new file mode 100644 index 00000000000..21f572d0c5d --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/WeakSignalView.swift @@ -0,0 +1,63 @@ +import Foundation +import UIKit +import Display + +final class WeakSignalView: OverlayMaskContainerView { + private struct Params: Equatable { + var constrainedSize: CGSize + + init(constrainedSize: CGSize) { + self.constrainedSize = constrainedSize + } + } + private struct Layout { + var params: Params + var size: CGSize + + init(params: Params, size: CGSize) { + self.params = params + self.size = size + } + } + + private let titleView: TextView + private let overlayBackgroundView: UIImageView + + private var currentLayout: Layout? + + override init(frame: CGRect) { + self.titleView = TextView() + self.overlayBackgroundView = UIImageView() + + super.init(frame: frame) + + self.maskContents.addSubview(self.overlayBackgroundView) + self.addSubview(self.titleView) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(constrainedSize: CGSize) -> CGSize { + let params = Params(constrainedSize: constrainedSize) + if let currentLayout = self.currentLayout, currentLayout.params == params { + return currentLayout.size + } + + let sideInset: CGFloat = 11.0 + let height: CGFloat = 30.0 + + let titleSize = self.titleView.update(string: "Weak network signal", fontSize: 16.0, fontWeight: 0.0, color: .white, constrainedWidth: constrainedSize.width - sideInset * 2.0, transition: .immediate) + let size = CGSize(width: titleSize.width + sideInset * 2.0, height: height) + self.titleView.frame = CGRect(origin: CGPoint(x: sideInset, y: floor((size.height - titleSize.height) * 0.5)), size: titleSize) + + if self.overlayBackgroundView.image?.size.height != height { + self.overlayBackgroundView.image = generateStretchableFilledCircleImage(diameter: height, color: .white) + } + self.overlayBackgroundView.frame = CGRect(origin: CGPoint(), size: size) + + self.currentLayout = Layout(params: params, size: size) + return size + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/ContentOverlayLayer.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/ContentOverlayLayer.swift new file mode 100644 index 00000000000..445f16f6c49 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/ContentOverlayLayer.swift @@ -0,0 +1,72 @@ +import Foundation +import UIKit +import Display + +final class ContentOverlayLayer: SimpleLayer { + private struct Params: Equatable { + var size: CGSize + var contentInsets: UIEdgeInsets + + init(size: CGSize, contentInsets: UIEdgeInsets) { + self.size = size + self.contentInsets = contentInsets + } + } + + var contentsLayer: CALayer? { + didSet { + if self.contentsLayer !== oldValue { + oldValue?.mask = nil + oldValue?.removeFromSuperlayer() + + if let contentsLayer = self.contentsLayer { + contentsLayer.mask = self.maskContentLayer + self.addSublayer(contentsLayer) + + if let params = self.params { + let size = params.size + let contentInsets = params.contentInsets + self.params = nil + self.update(size: size, contentInsets: contentInsets) + } + } + } + } + } + + let maskContentLayer: SimpleLayer + + private var params: Params? + + override init() { + self.maskContentLayer = SimpleLayer() + + super.init() + } + + override init(layer: Any) { + self.maskContentLayer = SimpleLayer() + + super.init(layer: layer) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(size: CGSize, contentInsets: UIEdgeInsets) { + let params = Params(size: size, contentInsets: contentInsets) + if self.params == params { + return + } + self.params = params + + let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0) + + self.maskContentLayer.frame = CGRect(origin: CGPoint(x: contentInsets.left, y: contentInsets.top), size: size) + + if let contentsLayer = self.contentsLayer { + contentsLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width - (contentInsets.left + contentInsets.right), height: size.height - (contentInsets.top + contentInsets.bottom))) + } + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Media/VideoInput.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Media/VideoInput.swift new file mode 100644 index 00000000000..c15a7bc427f --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Media/VideoInput.swift @@ -0,0 +1,125 @@ +import AVFoundation +import Metal +import CoreVideo +import Display + +public final class VideoSourceOutput { + public let y: MTLTexture + public let uv: MTLTexture + public let rotationAngle: Float + public let sourceId: Int + + public init(y: MTLTexture, uv: MTLTexture, rotationAngle: Float, sourceId: Int) { + self.y = y + self.uv = uv + self.rotationAngle = rotationAngle + self.sourceId = sourceId + } +} + +public protocol VideoSource: AnyObject { + typealias Output = VideoSourceOutput + + var updated: (() -> Void)? { get set } + var currentOutput: Output? { get } +} + +public final class FileVideoSource: VideoSource { + private let playerLooper: AVPlayerLooper + private let queuePlayer: AVQueuePlayer + + private var videoOutput: AVPlayerItemVideoOutput + private var device: MTLDevice + private var textureCache: CVMetalTextureCache? + + private var targetItem: AVPlayerItem? + + public private(set) var currentOutput: Output? + public var updated: (() -> Void)? + + private var displayLink: SharedDisplayLinkDriver.Link? + + public var sourceId: Int = 0 + + public init?(device: MTLDevice, url: URL) { + self.device = device + CVMetalTextureCacheCreate(nil, nil, device, nil, &self.textureCache) + + let playerItem = AVPlayerItem(url: url) + self.queuePlayer = AVQueuePlayer(playerItem: playerItem) + self.playerLooper = AVPlayerLooper(player: self.queuePlayer, templateItem: playerItem) + + let outputSettings: [String: Any] = [ + kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, + kCVPixelBufferMetalCompatibilityKey as String: true + ] + self.videoOutput = AVPlayerItemVideoOutput(outputSettings: outputSettings) + + self.queuePlayer.play() + + self.displayLink = SharedDisplayLinkDriver.shared.add(framesPerSecond: .fps(60), { [weak self] _ in + guard let self else { + return + } + if self.updateOutput() { + self.updated?() + } + }) + } + + private func updateOutput() -> Bool { + if self.targetItem !== self.queuePlayer.currentItem { + self.targetItem?.remove(self.videoOutput) + self.targetItem = self.queuePlayer.currentItem + if let targetItem = self.targetItem { + targetItem.add(self.videoOutput) + } + } + + guard let currentItem = self.targetItem else { + return false + } + + let currentTime = currentItem.currentTime() + guard self.videoOutput.hasNewPixelBuffer(forItemTime: currentTime) else { + return false + } + + var rotationAngle: Float = 0.0 + if currentTime.seconds <= currentItem.duration.seconds * 0.25 { + rotationAngle = 0.0 + } else if currentTime.seconds <= currentItem.duration.seconds * 0.5 { + rotationAngle = Float.pi * 0.5 + } else if currentTime.seconds <= currentItem.duration.seconds * 0.75 { + rotationAngle = Float.pi + } else { + rotationAngle = Float.pi * 3.0 / 2.0 + } + + var pixelBuffer: CVPixelBuffer? + pixelBuffer = self.videoOutput.copyPixelBuffer(forItemTime: currentTime, itemTimeForDisplay: nil) + + guard let buffer = pixelBuffer else { + return false + } + + let width = CVPixelBufferGetWidth(buffer) + let height = CVPixelBufferGetHeight(buffer) + + var cvMetalTextureY: CVMetalTexture? + var status = CVMetalTextureCacheCreateTextureFromImage(nil, self.textureCache!, buffer, nil, .r8Unorm, width, height, 0, &cvMetalTextureY) + guard status == kCVReturnSuccess, let yTexture = CVMetalTextureGetTexture(cvMetalTextureY!) else { + return false + } + var cvMetalTextureUV: CVMetalTexture? + status = CVMetalTextureCacheCreateTextureFromImage(nil, self.textureCache!, buffer, nil, .rg8Unorm, width / 2, height / 2, 1, &cvMetalTextureUV) + guard status == kCVReturnSuccess, let uvTexture = CVMetalTextureGetTexture(cvMetalTextureUV!) else { + return false + } + + rotationAngle = Float.pi * 0.5 + + self.currentOutput = Output(y: yTexture, uv: uvTexture, rotationAngle: rotationAngle, sourceId: self.sourceId) + return true + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/MirroringLayer.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/MirroringLayer.swift new file mode 100644 index 00000000000..8506d054454 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/MirroringLayer.swift @@ -0,0 +1,98 @@ +import Foundation +import UIKit +import Display + +final class MirroringLayer: SimpleLayer { + var targetLayer: CALayer? + + override init() { + super.init() + } + + override init(layer: Any) { + super.init(layer: layer) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override var position: CGPoint { + get { + return super.position + } set(value) { + if let targetLayer = self.targetLayer { + targetLayer.position = value + } + super.position = value + } + } + + override var bounds: CGRect { + get { + return super.bounds + } set(value) { + if let targetLayer = self.targetLayer { + targetLayer.bounds = value + } + super.bounds = value + } + } + + override var opacity: Float { + get { + return super.opacity + } set(value) { + if let targetLayer = self.targetLayer { + targetLayer.opacity = value + } + super.opacity = value + } + } + + override public var sublayerTransform: CATransform3D { + get { + return super.sublayerTransform + } set(value) { + if let targetLayer = self.targetLayer { + targetLayer.sublayerTransform = value + } + super.sublayerTransform = value + } + } + + override public var transform: CATransform3D { + get { + return super.transform + } set(value) { + if let targetLayer = self.targetLayer { + targetLayer.transform = value + } + super.transform = value + } + } + + override public func add(_ animation: CAAnimation, forKey key: String?) { + if let targetLayer = self.targetLayer { + targetLayer.add(animation, forKey: key) + } + + super.add(animation, forKey: key) + } + + override public func removeAllAnimations() { + if let targetLayer = self.targetLayer { + targetLayer.removeAllAnimations() + } + + super.removeAllAnimations() + } + + override public func removeAnimation(forKey: String) { + if let targetLayer = self.targetLayer { + targetLayer.removeAnimation(forKey: forKey) + } + + super.removeAnimation(forKey: forKey) + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/OverlayMaskContainerView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/OverlayMaskContainerView.swift new file mode 100644 index 00000000000..447704d0dc0 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/OverlayMaskContainerView.swift @@ -0,0 +1,68 @@ +import Foundation +import UIKit + +public protocol OverlayMaskContainerViewProtocol: UIView { + var maskContents: UIView { get } +} + +public class OverlayMaskContainerView: UIView, OverlayMaskContainerViewProtocol { + override public static var layerClass: AnyClass { + return MirroringLayer.self + } + + public let maskContents: UIView + + override init(frame: CGRect) { + self.maskContents = UIView() + + super.init(frame: frame) + + (self.layer as? MirroringLayer)?.targetLayer = self.maskContents.layer + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override public func addSubview(_ view: UIView) { + super.addSubview(view) + + if let view = view as? OverlayMaskContainerViewProtocol { + self.maskContents.addSubview(view.maskContents) + } + } + + override public func insertSubview(_ view: UIView, at index: Int) { + super.insertSubview(view, at: index) + + if let view = view as? OverlayMaskContainerViewProtocol { + self.maskContents.addSubview(view.maskContents) + } + } + + override public func insertSubview(_ view: UIView, aboveSubview siblingSubview: UIView) { + super.insertSubview(view, aboveSubview: siblingSubview) + + if let view = view as? OverlayMaskContainerViewProtocol { + self.maskContents.addSubview(view.maskContents) + } + } + + override public func insertSubview(_ view: UIView, belowSubview siblingSubview: UIView) { + super.insertSubview(view, belowSubview: siblingSubview) + + if let view = view as? OverlayMaskContainerViewProtocol { + self.maskContents.addSubview(view.maskContents) + } + } + + override public func willRemoveSubview(_ subview: UIView) { + super.willRemoveSubview(subview) + + if let view = subview as? OverlayMaskContainerViewProtocol { + if view.maskContents.superview === self { + view.maskContents.removeFromSuperview() + } + } + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift new file mode 100644 index 00000000000..adfc27257f5 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift @@ -0,0 +1,715 @@ +import Foundation +import UIKit +import Display +import MetalEngine +import ComponentFlow +import SwiftSignalKit + +public final class PrivateCallScreen: OverlayMaskContainerView { + public struct State: Equatable { + public struct SignalInfo: Equatable { + public var quality: Double + + public init(quality: Double) { + self.quality = quality + } + } + + public struct ActiveState: Equatable { + public var startTime: Double + public var signalInfo: SignalInfo + public var emojiKey: [String] + + public init(startTime: Double, signalInfo: SignalInfo, emojiKey: [String]) { + self.startTime = startTime + self.signalInfo = signalInfo + self.emojiKey = emojiKey + } + } + + public struct TerminatedState: Equatable { + public var duration: Double + + public init(duration: Double) { + self.duration = duration + } + } + + public enum LifecycleState: Equatable { + case connecting + case ringing + case exchangingKeys + case active(ActiveState) + case terminated(TerminatedState) + } + + public enum AudioOutput: Equatable { + case internalSpeaker + case speaker + } + + public var lifecycleState: LifecycleState + public var name: String + public var avatarImage: UIImage? + public var audioOutput: AudioOutput + public var isMicrophoneMuted: Bool + public var localVideo: VideoSource? + public var remoteVideo: VideoSource? + + public init( + lifecycleState: LifecycleState, + name: String, + avatarImage: UIImage?, + audioOutput: AudioOutput, + isMicrophoneMuted: Bool, + localVideo: VideoSource?, + remoteVideo: VideoSource? + ) { + self.lifecycleState = lifecycleState + self.name = name + self.avatarImage = avatarImage + self.audioOutput = audioOutput + self.isMicrophoneMuted = isMicrophoneMuted + self.localVideo = localVideo + self.remoteVideo = remoteVideo + } + + public static func ==(lhs: State, rhs: State) -> Bool { + if lhs.lifecycleState != rhs.lifecycleState { + return false + } + if lhs.name != rhs.name { + return false + } + if lhs.avatarImage != rhs.avatarImage { + return false + } + if lhs.audioOutput != rhs.audioOutput { + return false + } + if lhs.isMicrophoneMuted != rhs.isMicrophoneMuted { + return false + } + if lhs.localVideo !== rhs.localVideo { + return false + } + if lhs.remoteVideo !== rhs.remoteVideo { + return false + } + return true + } + } + + private struct Params: Equatable { + var size: CGSize + var insets: UIEdgeInsets + var screenCornerRadius: CGFloat + var state: State + + init(size: CGSize, insets: UIEdgeInsets, screenCornerRadius: CGFloat, state: State) { + self.size = size + self.insets = insets + self.screenCornerRadius = screenCornerRadius + self.state = state + } + } + + private var params: Params? + + private let backgroundLayer: CallBackgroundLayer + private let overlayContentsView: UIView + private let buttonGroupView: ButtonGroupView + private let blobLayer: CallBlobsLayer + private let avatarLayer: AvatarLayer + private let titleView: TextView + + private var statusView: StatusView + private var weakSignalView: WeakSignalView? + + private var emojiView: KeyEmojiView? + + private var localVideoContainerView: VideoContainerView? + private var remoteVideoContainerView: VideoContainerView? + + private var activeRemoteVideoSource: VideoSource? + private var waitingForFirstRemoteVideoFrameDisposable: Disposable? + + private var activeLocalVideoSource: VideoSource? + private var waitingForFirstLocalVideoFrameDisposable: Disposable? + + private var processedInitialAudioLevelBump: Bool = false + private var audioLevelBump: Float = 0.0 + + private var targetAudioLevel: Float = 0.0 + private var audioLevel: Float = 0.0 + private var audioLevelUpdateSubscription: SharedDisplayLinkDriver.Link? + + public var speakerAction: (() -> Void)? + public var flipCameraAction: (() -> Void)? + public var videoAction: (() -> Void)? + public var microhoneMuteAction: (() -> Void)? + public var endCallAction: (() -> Void)? + + public override init(frame: CGRect) { + self.overlayContentsView = UIView() + self.overlayContentsView.isUserInteractionEnabled = false + + self.backgroundLayer = CallBackgroundLayer() + + self.buttonGroupView = ButtonGroupView() + + self.blobLayer = CallBlobsLayer() + self.avatarLayer = AvatarLayer() + + self.titleView = TextView() + self.statusView = StatusView() + + super.init(frame: frame) + + self.layer.addSublayer(self.backgroundLayer) + self.overlayContentsView.layer.addSublayer(self.backgroundLayer.blurredLayer) + + self.layer.addSublayer(self.blobLayer) + self.layer.addSublayer(self.avatarLayer) + + self.overlayContentsView.mask = self.maskContents + self.addSubview(self.overlayContentsView) + + self.addSubview(self.buttonGroupView) + + self.addSubview(self.titleView) + + self.addSubview(self.statusView) + self.statusView.requestLayout = { [weak self] in + self?.update(transition: .immediate) + } + + (self.layer as? SimpleLayer)?.didEnterHierarchy = { [weak self] in + guard let self else { + return + } + self.audioLevelUpdateSubscription = SharedDisplayLinkDriver.shared.add { [weak self] _ in + guard let self else { + return + } + self.attenuateAudioLevelStep() + } + } + (self.layer as? SimpleLayer)?.didExitHierarchy = { [weak self] in + guard let self else { + return + } + self.audioLevelUpdateSubscription = nil + } + } + + public required init?(coder: NSCoder) { + fatalError() + } + + deinit { + self.waitingForFirstRemoteVideoFrameDisposable?.dispose() + self.waitingForFirstLocalVideoFrameDisposable?.dispose() + } + + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + guard let result = super.hitTest(point, with: event) else { + return nil + } + + return result + } + + public func addIncomingAudioLevel(value: Float) { + self.targetAudioLevel = value + } + + private func attenuateAudioLevelStep() { + self.audioLevel = self.audioLevel * 0.8 + (self.targetAudioLevel + self.audioLevelBump) * 0.2 + if self.audioLevel <= 0.01 { + self.audioLevel = 0.0 + } + self.updateAudioLevel() + } + + private func updateAudioLevel() { + if self.activeRemoteVideoSource == nil && self.activeLocalVideoSource == nil { + let additionalAvatarScale = CGFloat(max(0.0, min(self.audioLevel, 5.0)) * 0.05) + self.avatarLayer.transform = CATransform3DMakeScale(1.0 + additionalAvatarScale, 1.0 + additionalAvatarScale, 1.0) + + if let params = self.params, case .terminated = params.state.lifecycleState { + } else { + let blobAmplificationFactor: CGFloat = 2.0 + self.blobLayer.transform = CATransform3DMakeScale(1.0 + additionalAvatarScale * blobAmplificationFactor, 1.0 + additionalAvatarScale * blobAmplificationFactor, 1.0) + } + } + } + + public func update(size: CGSize, insets: UIEdgeInsets, screenCornerRadius: CGFloat, state: State, transition: Transition) { + let params = Params(size: size, insets: insets, screenCornerRadius: screenCornerRadius, state: state) + if self.params == params { + return + } + + if self.params?.state.remoteVideo !== params.state.remoteVideo { + self.waitingForFirstRemoteVideoFrameDisposable?.dispose() + + if let remoteVideo = params.state.remoteVideo { + if remoteVideo.currentOutput != nil { + self.activeRemoteVideoSource = remoteVideo + } else { + let firstVideoFrameSignal = Signal { subscriber in + remoteVideo.updated = { [weak remoteVideo] in + guard let remoteVideo else { + subscriber.putCompletion() + return + } + if remoteVideo.currentOutput != nil { + subscriber.putCompletion() + } + } + + return EmptyDisposable + } + var shouldUpdate = false + self.waitingForFirstRemoteVideoFrameDisposable = (firstVideoFrameSignal + |> timeout(4.0, queue: .mainQueue(), alternate: .complete()) + |> deliverOnMainQueue).startStrict(completed: { [weak self] in + guard let self else { + return + } + self.activeRemoteVideoSource = remoteVideo + if shouldUpdate { + self.update(transition: .spring(duration: 0.3)) + } + }) + shouldUpdate = true + } + } else { + self.activeRemoteVideoSource = nil + } + } + if self.params?.state.localVideo !== params.state.localVideo { + self.waitingForFirstLocalVideoFrameDisposable?.dispose() + + if let localVideo = params.state.localVideo { + if localVideo.currentOutput != nil { + self.activeLocalVideoSource = localVideo + } else { + let firstVideoFrameSignal = Signal { subscriber in + localVideo.updated = { [weak localVideo] in + guard let localVideo else { + subscriber.putCompletion() + return + } + if localVideo.currentOutput != nil { + subscriber.putCompletion() + } + } + + return EmptyDisposable + } + var shouldUpdate = false + self.waitingForFirstLocalVideoFrameDisposable = (firstVideoFrameSignal + |> timeout(4.0, queue: .mainQueue(), alternate: .complete()) + |> deliverOnMainQueue).startStrict(completed: { [weak self] in + guard let self else { + return + } + self.activeLocalVideoSource = localVideo + if shouldUpdate { + self.update(transition: .spring(duration: 0.3)) + } + }) + shouldUpdate = true + } + } else { + self.activeLocalVideoSource = nil + } + } + + self.params = params + self.updateInternal(params: params, transition: transition) + } + + private func update(transition: Transition) { + guard let params = self.params else { + return + } + self.updateInternal(params: params, transition: transition) + } + + private func updateInternal(params: Params, transition: Transition) { + let backgroundFrame = CGRect(origin: CGPoint(), size: params.size) + + let aspect: CGFloat = params.size.width / params.size.height + let sizeNorm: CGFloat = 64.0 + let renderingSize = CGSize(width: floor(sizeNorm * aspect), height: sizeNorm) + let edgeSize: Int = 2 + + let primaryVideoSource: VideoSource? + let secondaryVideoSource: VideoSource? + if let activeRemoteVideoSource = self.activeRemoteVideoSource, let activeLocalVideoSource = self.activeLocalVideoSource { + primaryVideoSource = activeRemoteVideoSource + secondaryVideoSource = activeLocalVideoSource + } else if let activeRemoteVideoSource = self.activeRemoteVideoSource { + primaryVideoSource = activeRemoteVideoSource + secondaryVideoSource = nil + } else if let activeLocalVideoSource = self.activeLocalVideoSource { + primaryVideoSource = activeLocalVideoSource + secondaryVideoSource = nil + } else { + primaryVideoSource = nil + secondaryVideoSource = nil + } + + let havePrimaryVideo = self.activeRemoteVideoSource != nil || self.activeLocalVideoSource != nil + + let visualBackgroundFrame = backgroundFrame.insetBy(dx: -CGFloat(edgeSize) / renderingSize.width * backgroundFrame.width, dy: -CGFloat(edgeSize) / renderingSize.height * backgroundFrame.height) + + self.backgroundLayer.renderSpec = RenderLayerSpec(size: RenderSize(width: Int(renderingSize.width) + edgeSize * 2, height: Int(renderingSize.height) + edgeSize * 2)) + transition.setFrame(layer: self.backgroundLayer, frame: visualBackgroundFrame) + transition.setFrame(layer: self.backgroundLayer.blurredLayer, frame: visualBackgroundFrame) + + let backgroundStateIndex: Int + switch params.state.lifecycleState { + case .connecting: + backgroundStateIndex = 0 + case .ringing: + backgroundStateIndex = 0 + case .exchangingKeys: + backgroundStateIndex = 0 + case let .active(activeState): + if activeState.signalInfo.quality <= 0.2 { + backgroundStateIndex = 2 + } else { + backgroundStateIndex = 1 + } + case .terminated: + backgroundStateIndex = 0 + } + self.backgroundLayer.update(stateIndex: backgroundStateIndex, transition: transition) + + transition.setFrame(view: self.buttonGroupView, frame: CGRect(origin: CGPoint(), size: params.size)) + + var buttons: [ButtonGroupView.Button] = [ + ButtonGroupView.Button(content: .video(isActive: params.state.localVideo != nil), action: { [weak self] in + guard let self else { + return + } + self.videoAction?() + }), + ButtonGroupView.Button(content: .microphone(isMuted: params.state.isMicrophoneMuted), action: { [weak self] in + guard let self else { + return + } + self.microhoneMuteAction?() + }), + ButtonGroupView.Button(content: .end, action: { [weak self] in + guard let self else { + return + } + self.endCallAction?() + }) + ] + if self.activeLocalVideoSource != nil { + buttons.insert(ButtonGroupView.Button(content: .flipCamera, action: { [weak self] in + guard let self else { + return + } + self.flipCameraAction?() + }), at: 0) + } else { + buttons.insert(ButtonGroupView.Button(content: .speaker(isActive: params.state.audioOutput != .internalSpeaker), action: { [weak self] in + guard let self else { + return + } + self.speakerAction?() + }), at: 0) + } + self.buttonGroupView.update(size: params.size, buttons: buttons, transition: transition) + + if case let .active(activeState) = params.state.lifecycleState { + let emojiView: KeyEmojiView + var emojiTransition = transition + if let current = self.emojiView { + emojiView = current + } else { + emojiTransition = transition.withAnimation(.none) + emojiView = KeyEmojiView(emoji: activeState.emojiKey) + self.emojiView = emojiView + } + if emojiView.superview == nil { + self.addSubview(emojiView) + if !transition.animation.isImmediate { + emojiView.animateIn() + } + } + emojiTransition.setFrame(view: emojiView, frame: CGRect(origin: CGPoint(x: params.size.width - params.insets.right - 12.0 - emojiView.size.width, y: params.insets.top + 27.0), size: emojiView.size)) + } else { + if let emojiView = self.emojiView { + self.emojiView = nil + transition.setAlpha(view: emojiView, alpha: 0.0, completion: { [weak emojiView] _ in + emojiView?.removeFromSuperview() + }) + } + } + + let collapsedAvatarSize: CGFloat = 136.0 + let blobSize: CGFloat = collapsedAvatarSize + 40.0 + + let collapsedAvatarFrame = CGRect(origin: CGPoint(x: floor((params.size.width - collapsedAvatarSize) * 0.5), y: 222.0), size: CGSize(width: collapsedAvatarSize, height: collapsedAvatarSize)) + let expandedAvatarFrame = CGRect(origin: CGPoint(), size: params.size) + let expandedVideoFrame = CGRect(origin: CGPoint(), size: params.size) + let avatarFrame = havePrimaryVideo ? expandedAvatarFrame : collapsedAvatarFrame + let avatarCornerRadius = havePrimaryVideo ? params.screenCornerRadius : collapsedAvatarSize * 0.5 + + let minimizedVideoInsets = UIEdgeInsets(top: 124.0, left: 12.0, bottom: 178.0, right: 12.0) + + if let primaryVideoSource { + let remoteVideoContainerView: VideoContainerView + if let current = self.remoteVideoContainerView { + remoteVideoContainerView = current + } else { + remoteVideoContainerView = VideoContainerView(frame: CGRect()) + self.remoteVideoContainerView = remoteVideoContainerView + self.insertSubview(remoteVideoContainerView, belowSubview: self.overlayContentsView) + self.overlayContentsView.layer.addSublayer(remoteVideoContainerView.blurredContainerLayer) + + remoteVideoContainerView.layer.position = self.avatarLayer.position + remoteVideoContainerView.layer.bounds = self.avatarLayer.bounds + remoteVideoContainerView.alpha = 0.0 + remoteVideoContainerView.blurredContainerLayer.position = self.avatarLayer.position + remoteVideoContainerView.blurredContainerLayer.bounds = self.avatarLayer.bounds + remoteVideoContainerView.blurredContainerLayer.opacity = 0.0 + remoteVideoContainerView.update(size: self.avatarLayer.bounds.size, insets: minimizedVideoInsets, cornerRadius: self.avatarLayer.params?.cornerRadius ?? 0.0, isMinimized: false, isAnimatingOut: false, transition: .immediate) + } + + if remoteVideoContainerView.video !== primaryVideoSource { + remoteVideoContainerView.video = primaryVideoSource + } + + transition.setPosition(view: remoteVideoContainerView, position: expandedVideoFrame.center) + transition.setBounds(view: remoteVideoContainerView, bounds: CGRect(origin: CGPoint(), size: expandedVideoFrame.size)) + transition.setAlpha(view: remoteVideoContainerView, alpha: 1.0) + transition.setPosition(layer: remoteVideoContainerView.blurredContainerLayer, position: expandedVideoFrame.center) + transition.setBounds(layer: remoteVideoContainerView.blurredContainerLayer, bounds: CGRect(origin: CGPoint(), size: expandedVideoFrame.size)) + transition.setAlpha(layer: remoteVideoContainerView.blurredContainerLayer, alpha: 1.0) + remoteVideoContainerView.update(size: expandedVideoFrame.size, insets: minimizedVideoInsets, cornerRadius: params.screenCornerRadius, isMinimized: false, isAnimatingOut: false, transition: transition) + } else { + if let remoteVideoContainerView = self.remoteVideoContainerView { + remoteVideoContainerView.update(size: avatarFrame.size, insets: minimizedVideoInsets, cornerRadius: avatarCornerRadius, isMinimized: false, isAnimatingOut: true, transition: transition) + transition.setPosition(layer: remoteVideoContainerView.blurredContainerLayer, position: avatarFrame.center) + transition.setBounds(layer: remoteVideoContainerView.blurredContainerLayer, bounds: CGRect(origin: CGPoint(), size: avatarFrame.size)) + transition.setAlpha(layer: remoteVideoContainerView.blurredContainerLayer, alpha: 0.0) + transition.setPosition(view: remoteVideoContainerView, position: avatarFrame.center) + transition.setBounds(view: remoteVideoContainerView, bounds: CGRect(origin: CGPoint(), size: avatarFrame.size)) + if remoteVideoContainerView.alpha != 0.0 { + transition.setAlpha(view: remoteVideoContainerView, alpha: 0.0, completion: { [weak self, weak remoteVideoContainerView] completed in + guard let self, let remoteVideoContainerView, completed else { + return + } + remoteVideoContainerView.removeFromSuperview() + remoteVideoContainerView.blurredContainerLayer.removeFromSuperlayer() + if self.remoteVideoContainerView === remoteVideoContainerView { + self.remoteVideoContainerView = nil + } + }) + } + } + } + + if let secondaryVideoSource { + let localVideoContainerView: VideoContainerView + if let current = self.localVideoContainerView { + localVideoContainerView = current + } else { + localVideoContainerView = VideoContainerView(frame: CGRect()) + self.localVideoContainerView = localVideoContainerView + self.insertSubview(localVideoContainerView, belowSubview: self.overlayContentsView) + self.overlayContentsView.layer.addSublayer(localVideoContainerView.blurredContainerLayer) + + localVideoContainerView.layer.position = self.avatarLayer.position + localVideoContainerView.layer.bounds = self.avatarLayer.bounds + localVideoContainerView.alpha = 0.0 + localVideoContainerView.blurredContainerLayer.position = self.avatarLayer.position + localVideoContainerView.blurredContainerLayer.bounds = self.avatarLayer.bounds + localVideoContainerView.blurredContainerLayer.opacity = 0.0 + localVideoContainerView.update(size: self.avatarLayer.bounds.size, insets: minimizedVideoInsets, cornerRadius: self.avatarLayer.params?.cornerRadius ?? 0.0, isMinimized: true, isAnimatingOut: false, transition: .immediate) + } + + if localVideoContainerView.video !== secondaryVideoSource { + localVideoContainerView.video = secondaryVideoSource + } + + transition.setPosition(view: localVideoContainerView, position: expandedVideoFrame.center) + transition.setBounds(view: localVideoContainerView, bounds: CGRect(origin: CGPoint(), size: expandedVideoFrame.size)) + transition.setAlpha(view: localVideoContainerView, alpha: 1.0) + transition.setPosition(layer: localVideoContainerView.blurredContainerLayer, position: expandedVideoFrame.center) + transition.setBounds(layer: localVideoContainerView.blurredContainerLayer, bounds: CGRect(origin: CGPoint(), size: expandedVideoFrame.size)) + transition.setAlpha(layer: localVideoContainerView.blurredContainerLayer, alpha: 1.0) + localVideoContainerView.update(size: expandedVideoFrame.size, insets: minimizedVideoInsets, cornerRadius: params.screenCornerRadius, isMinimized: true, isAnimatingOut: false, transition: transition) + } else { + if let localVideoContainerView = self.localVideoContainerView { + localVideoContainerView.update(size: avatarFrame.size, insets: minimizedVideoInsets, cornerRadius: avatarCornerRadius, isMinimized: false, isAnimatingOut: true, transition: transition) + transition.setPosition(layer: localVideoContainerView.blurredContainerLayer, position: avatarFrame.center) + transition.setBounds(layer: localVideoContainerView.blurredContainerLayer, bounds: CGRect(origin: CGPoint(), size: avatarFrame.size)) + transition.setAlpha(layer: localVideoContainerView.blurredContainerLayer, alpha: 0.0) + transition.setPosition(view: localVideoContainerView, position: avatarFrame.center) + transition.setBounds(view: localVideoContainerView, bounds: CGRect(origin: CGPoint(), size: avatarFrame.size)) + if localVideoContainerView.alpha != 0.0 { + transition.setAlpha(view: localVideoContainerView, alpha: 0.0, completion: { [weak self, weak localVideoContainerView] completed in + guard let self, let localVideoContainerView, completed else { + return + } + localVideoContainerView.removeFromSuperview() + localVideoContainerView.blurredContainerLayer.removeFromSuperlayer() + if self.localVideoContainerView === localVideoContainerView { + self.localVideoContainerView = nil + } + }) + } + } + } + + if self.avatarLayer.image !== params.state.avatarImage { + self.avatarLayer.image = params.state.avatarImage + } + transition.setPosition(layer: self.avatarLayer, position: avatarFrame.center) + transition.setBounds(layer: self.avatarLayer, bounds: CGRect(origin: CGPoint(), size: avatarFrame.size)) + self.avatarLayer.update(size: collapsedAvatarFrame.size, isExpanded:havePrimaryVideo, cornerRadius: avatarCornerRadius, transition: transition) + + let blobFrame = CGRect(origin: CGPoint(x: floor(avatarFrame.midX - blobSize * 0.5), y: floor(avatarFrame.midY - blobSize * 0.5)), size: CGSize(width: blobSize, height: blobSize)) + transition.setPosition(layer: self.blobLayer, position: CGPoint(x: blobFrame.midX, y: blobFrame.midY)) + transition.setBounds(layer: self.blobLayer, bounds: CGRect(origin: CGPoint(), size: blobFrame.size)) + + let titleString: String + switch params.state.lifecycleState { + case .terminated: + titleString = "Call Ended" + if !transition.animation.isImmediate { + transition.withAnimation(.curve(duration: 0.3, curve: .easeInOut)).setScale(layer: self.blobLayer, scale: 0.3) + } else { + transition.setScale(layer: self.blobLayer, scale: 0.3) + } + transition.setAlpha(layer: self.blobLayer, alpha: 0.0) + default: + titleString = params.state.name + transition.setAlpha(layer: self.blobLayer, alpha: 1.0) + } + + let titleSize = self.titleView.update( + string: titleString, + fontSize: !havePrimaryVideo ? 28.0 : 17.0, + fontWeight: !havePrimaryVideo ? 0.0 : 0.25, + color: .white, + constrainedWidth: params.size.width - 16.0 * 2.0, + transition: transition + ) + let titleFrame = CGRect( + origin: CGPoint( + x: (params.size.width - titleSize.width) * 0.5, + y: !havePrimaryVideo ? collapsedAvatarFrame.maxY + 39.0 : params.insets.top + 17.0 + ), + size: titleSize + ) + transition.setFrame(view: self.titleView, frame: titleFrame) + + let statusState: StatusView.State + switch params.state.lifecycleState { + case .connecting: + statusState = .waiting(.requesting) + case .ringing: + statusState = .waiting(.ringing) + case .exchangingKeys: + statusState = .waiting(.generatingKeys) + case let .active(activeState): + statusState = .active(StatusView.ActiveState(startTimestamp: activeState.startTime, signalStrength: activeState.signalInfo.quality)) + + if !self.processedInitialAudioLevelBump { + self.processedInitialAudioLevelBump = true + self.audioLevelBump = 2.0 + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.2, execute: { [weak self] in + guard let self else { + return + } + self.audioLevelBump = 0.0 + }) + } + case let .terminated(terminatedState): + self.processedInitialAudioLevelBump = false + statusState = .terminated(StatusView.TerminatedState(duration: terminatedState.duration)) + } + + if let previousState = self.statusView.state, previousState.key != statusState.key { + let previousStatusView = self.statusView + if !transition.animation.isImmediate { + transition.setPosition(view: previousStatusView, position: CGPoint(x: previousStatusView.center.x, y: previousStatusView.center.y - 5.0)) + transition.setScale(view: previousStatusView, scale: 0.5) + Transition.easeInOut(duration: 0.1).setAlpha(view: previousStatusView, alpha: 0.0, completion: { [weak previousStatusView] _ in + previousStatusView?.removeFromSuperview() + }) + } else { + previousStatusView.removeFromSuperview() + } + + self.statusView = StatusView() + self.insertSubview(self.statusView, aboveSubview: previousStatusView) + self.statusView.requestLayout = { [weak self] in + self?.update(transition: .immediate) + } + } + + let statusSize = self.statusView.update(state: statusState, transition: .immediate) + let statusFrame = CGRect( + origin: CGPoint( + x: (params.size.width - statusSize.width) * 0.5, + y: titleFrame.maxY + (havePrimaryVideo ? 0.0 : 4.0) + ), + size: statusSize + ) + if self.statusView.bounds.isEmpty { + self.statusView.frame = statusFrame + + if !transition.animation.isImmediate { + transition.animatePosition(view: self.statusView, from: CGPoint(x: 0.0, y: 5.0), to: CGPoint(), additive: true) + transition.animateScale(view: self.statusView, from: 0.5, to: 1.0) + Transition.easeInOut(duration: 0.15).animateAlpha(view: self.statusView, from: 0.0, to: 1.0) + } + } else { + transition.setFrame(view: self.statusView, frame: statusFrame) + } + + if case let .active(activeState) = params.state.lifecycleState, activeState.signalInfo.quality <= 0.2 { + let weakSignalView: WeakSignalView + if let current = self.weakSignalView { + weakSignalView = current + } else { + weakSignalView = WeakSignalView() + self.weakSignalView = weakSignalView + self.addSubview(weakSignalView) + } + let weakSignalSize = weakSignalView.update(constrainedSize: CGSize(width: params.size.width - 32.0, height: 100.0)) + let weakSignalFrame = CGRect(origin: CGPoint(x: floor((params.size.width - weakSignalSize.width) * 0.5), y: statusFrame.maxY + (havePrimaryVideo ? 12.0 : 12.0)), size: weakSignalSize) + if weakSignalView.bounds.isEmpty { + weakSignalView.frame = weakSignalFrame + if !transition.animation.isImmediate { + Transition.immediate.setScale(view: weakSignalView, scale: 0.001) + weakSignalView.alpha = 0.0 + transition.setScaleWithSpring(view: weakSignalView, scale: 1.0) + transition.setAlpha(view: weakSignalView, alpha: 1.0) + } + } else { + transition.setFrame(view: weakSignalView, frame: weakSignalFrame) + } + } else { + if let weakSignalView = self.weakSignalView { + self.weakSignalView = nil + transition.setScale(view: weakSignalView, scale: 0.001) + transition.setAlpha(view: weakSignalView, alpha: 0.0, completion: { [weak weakSignalView] _ in + weakSignalView?.removeFromSuperview() + }) + } + } + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Utils/Interpolate.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Utils/Interpolate.swift new file mode 100644 index 00000000000..21fbcc2a0f8 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Utils/Interpolate.swift @@ -0,0 +1,9 @@ +import Foundation + +func interpolateFloat(_ value1: Float, _ value2: Float, at factor: Float) -> Float { + return value1 * (1.0 - factor) + value2 * factor +} + +func interpolatePoints(_ point1: SIMD2, _ point2: SIMD2, at factor: Float) -> SIMD2 { + return SIMD2(x: interpolateFloat(point1.x, point2.x, at: factor), y: interpolateFloat(point1.y, point2.y, at: factor)) +} diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift index ef03a997099..83d72e27212 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift @@ -674,7 +674,19 @@ private final class CameraScreenComponent: CombinedComponent { self.resultDisposable.set((camera.stopRecording() |> deliverOnMainQueue).start(next: { [weak self] result in if let self, case let .finished(mainResult, additionalResult, duration, positionChangeTimestamps, _) = result { - self.completion.invoke(.single(.video(CameraScreen.Result.Video(videoPath: mainResult.0, coverImage: mainResult.1, mirror: mainResult.2, additionalVideoPath: additionalResult?.0, additionalCoverImage: additionalResult?.1, dimensions: PixelDimensions(mainResult.3), duration: duration, positionChangeTimestamps: positionChangeTimestamps, additionalVideoPosition: .topRight)))) + self.completion.invoke(.single( + .video(CameraScreen.Result.Video( + videoPath: mainResult.path, + coverImage: mainResult.thumbnail, + mirror: mainResult.isMirrored, + additionalVideoPath: additionalResult?.path, + additionalCoverImage: additionalResult?.thumbnail, + dimensions: PixelDimensions(mainResult.dimensions), + duration: duration, + positionChangeTimestamps: positionChangeTimestamps, + additionalVideoPosition: .topRight + )) + )) } })) self.isTransitioning = true @@ -1123,7 +1135,7 @@ private final class CameraScreenComponent: CombinedComponent { component: MultilineTextComponent( text: .plain(NSAttributedString(string: durationString, font: Font.with(size: 21.0, design: .camera), textColor: controlsTintColor)), horizontalAlignment: .center, - textShadowColor: UIColor(rgb: 0x000000, alpha: 0.2) + textShadowColor: controlsTintColor == .black ? .clear : UIColor(rgb: 0x000000, alpha: 0.2) ), availableSize: context.availableSize, transition: context.transition @@ -1733,8 +1745,7 @@ public class CameraScreen: ViewController { isDualEnabled: self.cameraState.isDualCameraEnabled, audio: true, photo: true, - metadata: false, - preferredFps: 60.0 + metadata: false ), previewView: self.mainPreviewView, secondaryPreviewView: self.additionalPreviewView diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/ShutterBlobView.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/ShutterBlobView.swift index 1d5e0b34d66..f0c69d9a5ec 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/ShutterBlobView.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/ShutterBlobView.swift @@ -279,7 +279,7 @@ final class ShutterBlobView: UIView { self.isOpaque = false self.backgroundColor = .clear - self.displayLink = SharedDisplayLinkDriver.shared.add { [weak self] in + self.displayLink = SharedDisplayLinkDriver.shared.add { [weak self] _ in self?.tick() } self.displayLink?.isPaused = true diff --git a/submodules/TelegramUI/Components/Chat/ChatBotInfoItem/Sources/ChatBotInfoItem.swift b/submodules/TelegramUI/Components/Chat/ChatBotInfoItem/Sources/ChatBotInfoItem.swift index c755c7aa445..6a9820e0ebf 100644 --- a/submodules/TelegramUI/Components/Chat/ChatBotInfoItem/Sources/ChatBotInfoItem.swift +++ b/submodules/TelegramUI/Components/Chat/ChatBotInfoItem/Sources/ChatBotInfoItem.swift @@ -459,7 +459,7 @@ public final class ChatBotInfoItemNode: ListViewItemNode { case let .peerMention(peerId, _, _): if let item = self.item { let _ = (item.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) - |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in + |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in if let peer = peer { self?.item?.controllerInteraction.openPeer(peer, .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) } diff --git a/submodules/TelegramUI/Components/Chat/ChatButtonKeyboardInputNode/Sources/ChatButtonKeyboardInputNode.swift b/submodules/TelegramUI/Components/Chat/ChatButtonKeyboardInputNode/Sources/ChatButtonKeyboardInputNode.swift index 3c7dfab0a4d..348fc553ecc 100644 --- a/submodules/TelegramUI/Components/Chat/ChatButtonKeyboardInputNode/Sources/ChatButtonKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatButtonKeyboardInputNode/Sources/ChatButtonKeyboardInputNode.swift @@ -430,7 +430,7 @@ public final class ChatButtonKeyboardInputNode: ChatInputNode { guard let self, let peer else { return } - self.controllerInteraction.openPeer(peer, .info, nil, .default) + self.controllerInteraction.openPeer(peer, .info(nil), nil, .default) }) case let .openWebView(url, simple): self.controllerInteraction.openWebView(markupButton.title, url, simple, .generic) diff --git a/submodules/TelegramUI/Components/Chat/ChatHistoryEntry/Sources/ChatHistoryEntry.swift b/submodules/TelegramUI/Components/Chat/ChatHistoryEntry/Sources/ChatHistoryEntry.swift index ee01ff53fd2..809deb1ba64 100644 --- a/submodules/TelegramUI/Components/Chat/ChatHistoryEntry/Sources/ChatHistoryEntry.swift +++ b/submodules/TelegramUI/Components/Chat/ChatHistoryEntry/Sources/ChatHistoryEntry.swift @@ -108,6 +108,17 @@ public enum ChatHistoryEntry: Identifiable, Comparable { return MessageIndex.absoluteLowerBound() } } + + public var timestamp: Int32? { + switch self { + case let .MessageEntry(message, _, _, _, _, _): + return message.timestamp + case let .MessageGroupEntry(_, messages, _): + return messages[0].0.timestamp + default: + return nil + } + } public static func ==(lhs: ChatHistoryEntry, rhs: ChatHistoryEntry) -> Bool { switch lhs { diff --git a/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/BUILD new file mode 100644 index 00000000000..f192f2f4096 --- /dev/null +++ b/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/BUILD @@ -0,0 +1,30 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ChatHistorySearchContainerNode", + module_name = "ChatHistorySearchContainerNode", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/Postbox", + "//submodules/TelegramCore", + "//submodules/TelegramPresentationData", + "//submodules/MergeLists", + "//submodules/AccountContext", + "//submodules/SearchUI", + "//submodules/TelegramUIPreferences", + "//submodules/ListMessageItem", + "//submodules/TelegramUI/Components/ChatControllerInteraction", + "//submodules/TelegramUI/Components/Chat/ChatMessageItemView", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Sources/ChatHistorySearchContainerNode.swift b/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift similarity index 89% rename from submodules/TelegramUI/Sources/ChatHistorySearchContainerNode.swift rename to submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift index e1beffa6d4d..2dd91738882 100644 --- a/submodules/TelegramUI/Sources/ChatHistorySearchContainerNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift @@ -14,6 +14,26 @@ import ListMessageItem import ChatControllerInteraction import ChatMessageItemView +private extension ListMessageItemInteraction { + convenience init(controllerInteraction: ChatControllerInteraction) { + self.init(openMessage: { message, mode -> Bool in + return controllerInteraction.openMessage(message, OpenMessageParams(mode: mode)) + }, openMessageContextMenu: { message, bool, node, rect, gesture in + controllerInteraction.openMessageContextMenu(message, bool, node, rect, gesture, nil) + }, toggleMessagesSelection: { messageId, selected in + controllerInteraction.toggleMessagesSelection(messageId, selected) + }, openUrl: { url, param1, param2, message in + controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: url, concealed: param1, external: param2, message: message)) + }, openInstantPage: { message, data in + controllerInteraction.openInstantPage(message, data) + }, longTap: { action, message in + controllerInteraction.longTap(action, message) + }, getHiddenMedia: { + return controllerInteraction.hiddenMedia + }) + } +} + private enum ChatHistorySearchEntryStableId: Hashable { case messageId(MessageId) } @@ -94,7 +114,7 @@ private func chatHistorySearchContainerPreparedTransition(from fromEntries: [Cha return ChatHistorySearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, query: query, displayingResults: displayingResults) } -final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { +public final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { private let context: AccountContext private let dimNode: ASDisplayNode @@ -106,7 +126,7 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { private var containerLayout: (ContainerViewLayout, CGFloat)? private var currentEntries: [ChatHistorySearchEntry]? - var currentMessages: [MessageId: Message]? + public var currentMessages: [MessageId: Message]? private var currentQuery: String? private let searchQuery = Promise() @@ -114,7 +134,7 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { private let searchDisposable = MetaDisposable() private let _isSearching = ValuePromise(false, ignoreRepeated: true) - override var isSearching: Signal { + override public var isSearching: Signal { return self._isSearching.get() } @@ -125,11 +145,11 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { private var enqueuedTransitions: [(ChatHistorySearchContainerTransition, Bool)] = [] - public override var hasDim: Bool { + override public var hasDim: Bool { return true } - init(context: AccountContext, peerId: PeerId, threadId: Int64?, tagMask: MessageTags, interfaceInteraction: ChatControllerInteraction) { + public init(context: AccountContext, peerId: PeerId, threadId: Int64?, tagMask: MessageTags, interfaceInteraction: ChatControllerInteraction) { self.context = context let presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -236,13 +256,13 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { self.searchDisposable.dispose() } - override func didLoad() { + override public func didLoad() { super.didLoad() self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:)))) } - override func searchTextUpdated(text: String) { + override public func searchTextUpdated(text: String) { if text.isEmpty { self.searchQuery.set(.single(nil)) } else { @@ -250,7 +270,7 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { } } - override func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { + override public func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { let firstValidLayout = self.containerLayout == nil self.containerLayout = (layout, navigationBarHeight) @@ -327,13 +347,13 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { } } - @objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) { + @objc private func dimTapGesture(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state { self.cancel?() } } - func messageForGallery(_ id: MessageId) -> Message? { + public func messageForGallery(_ id: MessageId) -> Message? { if let currentEntries = self.currentEntries { for entry in currentEntries { switch entry { @@ -347,7 +367,7 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { return nil } - func updateHiddenMedia() { + public func updateHiddenMedia() { self.listNode.forEachItemNode { itemNode in if let itemNode = itemNode as? ChatMessageItemView { itemNode.updateHiddenMedia() @@ -357,7 +377,7 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { } } - func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? { + public func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? { var transitionNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? self.listNode.forEachItemNode { itemNode in if let itemNode = itemNode as? ChatMessageItemView { diff --git a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift index 976d187e5b9..2c3823e1f38 100644 --- a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift @@ -26,7 +26,7 @@ public protocol ChatInputTextNodeDelegate: AnyObject { func chatInputTextNodeTargetForAction(action: Selector) -> ChatInputTextNode.TargetForAction? } -open class ChatInputTextNode: ASDisplayNode, UITextViewDelegate { +open class ChatInputTextNode: ASDisplayNode { public final class TargetForAction { public let target: Any? @@ -41,8 +41,6 @@ open class ChatInputTextNode: ASDisplayNode, UITextViewDelegate { } } - private var selectionChangedForEditedText: Bool = false - public var textView: ChatInputTextView { return self.view as! ChatInputTextView } @@ -122,30 +120,6 @@ open class ChatInputTextNode: ASDisplayNode, UITextViewDelegate { self.setViewBlock({ return ChatInputTextView(disableTiling: disableTiling) }) - - self.textView.delegate = self - self.textView.shouldRespondToAction = { [weak self] action in - guard let self, let action else { - return false - } - if let delegate = self.delegate { - return delegate.chatInputTextNodeShouldRespondToAction(action: action) - } else { - return true - } - } - self.textView.targetForAction = { [weak self] action in - guard let self, let action else { - return nil - } - if let delegate = self.delegate { - return delegate.chatInputTextNodeTargetForAction(action: action).flatMap { value in - return ChatInputTextViewImplTargetForAction(target: value.target) - } - } else { - return nil - } - } } public func resetInitialPrimaryLanguage() { @@ -155,52 +129,6 @@ open class ChatInputTextNode: ASDisplayNode, UITextViewDelegate { return self.textView.textHeightForWidth(width, rightInset: rightInset) } - @objc public func textViewDidBeginEditing(_ textView: UITextView) { - self.delegate?.chatInputTextNodeDidBeginEditing() - } - - @objc public func textViewDidEndEditing(_ textView: UITextView) { - self.delegate?.chatInputTextNodeDidFinishEditing() - } - - @objc public func textViewDidChange(_ textView: UITextView) { - self.selectionChangedForEditedText = true - - self.delegate?.chatInputTextNodeDidUpdateText() - - self.textView.updateTextContainerInset() - } - - @objc public func textViewDidChangeSelection(_ textView: UITextView) { - if self.textView.isPreservingSelection { - return - } - - self.selectionChangedForEditedText = false - - DispatchQueue.main.async { [weak self] in - guard let self else { - return - } - self.delegate?.chatInputTextNodeDidChangeSelection(dueToEditing: self.selectionChangedForEditedText) - } - } - - @available(iOS 16.0, *) - @objc public func textView(_ textView: UITextView, editMenuForTextIn range: NSRange, suggestedActions: [UIMenuElement]) -> UIMenu? { - return self.delegate?.chatInputTextNodeMenu(forTextRange: range, suggestedActions: suggestedActions) - } - - @objc public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { - guard let delegate = self.delegate else { - return true - } - if self.textView.isPreservingText { - return false - } - return delegate.chatInputTextNode(shouldChangeTextIn: range, replacementText: text) - } - public func updateLayout(size: CGSize) { self.textView.updateLayout(size: size) } @@ -267,7 +195,7 @@ private final class ChatInputTextContainer: NSTextContainer { } } -public final class ChatInputTextView: ChatInputTextViewImpl, NSLayoutManagerDelegate, NSTextStorageDelegate { +public final class ChatInputTextView: ChatInputTextViewImpl, UITextViewDelegate, NSLayoutManagerDelegate, NSTextStorageDelegate { public final class Theme: Equatable { public final class Quote: Equatable { public enum LineStyle: Equatable { @@ -387,6 +315,8 @@ public final class ChatInputTextView: ChatInputTextViewImpl, NSLayoutManagerDele private var didInitializePrimaryInputLanguage: Bool = false public var initialPrimaryLanguage: String? + private var selectionChangedForEditedText: Bool = false + override public var textInputMode: UITextInputMode? { if !self.didInitializePrimaryInputLanguage { self.didInitializePrimaryInputLanguage = true @@ -422,6 +352,31 @@ public final class ChatInputTextView: ChatInputTextViewImpl, NSLayoutManagerDele super.init(frame: CGRect(), textContainer: self.customTextContainer, disableTiling: disableTiling) + self.delegate = self + + self.shouldRespondToAction = { [weak self] action in + guard let self, let action else { + return false + } + if let delegate = self.customDelegate { + return delegate.chatInputTextNodeShouldRespondToAction(action: action) + } else { + return true + } + } + self.targetForAction = { [weak self] action in + guard let self, let action else { + return nil + } + if let delegate = self.customDelegate { + return delegate.chatInputTextNodeTargetForAction(action: action).flatMap { value in + return ChatInputTextViewImplTargetForAction(target: value.target) + } + } else { + return nil + } + } + self.textContainerInset = UIEdgeInsets() self.backgroundColor = nil self.isOpaque = false @@ -570,6 +525,54 @@ public final class ChatInputTextView: ChatInputTextViewImpl, NSLayoutManagerDele self.updateTextElements() } + @objc public func textViewDidBeginEditing(_ textView: UITextView) { + self.customDelegate?.chatInputTextNodeDidBeginEditing() + } + + @objc public func textViewDidEndEditing(_ textView: UITextView) { + self.customDelegate?.chatInputTextNodeDidFinishEditing() + } + + @objc public func textViewDidChange(_ textView: UITextView) { + self.selectionChangedForEditedText = true + + self.updateTextContainerInset() + + self.customDelegate?.chatInputTextNodeDidUpdateText() + + self.updateTextContainerInset() + } + + @objc public func textViewDidChangeSelection(_ textView: UITextView) { + if self.isPreservingSelection { + return + } + + self.selectionChangedForEditedText = false + + DispatchQueue.main.async { [weak self] in + guard let self else { + return + } + self.customDelegate?.chatInputTextNodeDidChangeSelection(dueToEditing: self.selectionChangedForEditedText) + } + } + + @available(iOS 16.0, *) + @objc public func textView(_ textView: UITextView, editMenuForTextIn range: NSRange, suggestedActions: [UIMenuElement]) -> UIMenu? { + return self.customDelegate?.chatInputTextNodeMenu(forTextRange: range, suggestedActions: suggestedActions) + } + + @objc public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { + guard let customDelegate = self.customDelegate else { + return true + } + if self.isPreservingText { + return false + } + return customDelegate.chatInputTextNode(shouldChangeTextIn: range, replacementText: text) + } + public func updateTextContainerInset() { var result = self.defaultTextContainerInset @@ -608,9 +611,16 @@ public final class ChatInputTextView: ChatInputTextViewImpl, NSLayoutManagerDele public func textHeightForWidth(_ width: CGFloat, rightInset: CGFloat) -> CGFloat { let measureSize = CGSize(width: width, height: 1000000.0) - if self.measurementTextStorage != self.attributedText || self.measurementTextContainer.size != measureSize || self.measurementTextContainer.rightInset != rightInset { + let measureText: NSAttributedString + if let attributedText = self.attributedText, attributedText.length != 0 { + measureText = attributedText + } else { + measureText = NSAttributedString(string: "A", attributes: self.typingAttributes) + } + + if self.measurementTextStorage != measureText || self.measurementTextContainer.size != measureSize || self.measurementTextContainer.rightInset != rightInset { self.measurementTextContainer.rightInset = rightInset - self.measurementTextStorage.setAttributedString(self.attributedText ?? NSAttributedString()) + self.measurementTextStorage.setAttributedString(measureText) self.measurementTextContainer.size = measureSize self.measurementLayoutManager.invalidateLayout(forCharacterRange: NSRange(location: 0, length: self.measurementTextStorage.length), actualCharacterRange: nil) self.measurementLayoutManager.ensureLayout(for: self.measurementTextContainer) @@ -618,7 +628,7 @@ public final class ChatInputTextView: ChatInputTextViewImpl, NSLayoutManagerDele let textSize = self.measurementLayoutManager.usedRect(for: self.measurementTextContainer).size - return textSize.height + self.textContainerInset.top + self.textContainerInset.bottom + return ceil(textSize.height + self.textContainerInset.top + self.textContainerInset.bottom) } public func updateLayout(size: CGSize) { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageActionBubbleContentNode/Sources/ChatMessageActionBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageActionBubbleContentNode/Sources/ChatMessageActionBubbleContentNode.swift index 3d49bde8e36..97f586634bd 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageActionBubbleContentNode/Sources/ChatMessageActionBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageActionBubbleContentNode/Sources/ChatMessageActionBubbleContentNode.swift @@ -227,11 +227,9 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode { } var backgroundSize = CGSize(width: labelLayout.size.width + 8.0 + 8.0, height: labelLayout.size.height + 4.0) - if let _ = image { backgroundSize.height += imageSize.height + 10 } - return (backgroundSize.width, { boundingWidth in return (backgroundSize, { [weak self] animation, synchronousLoads, _ in if let strongSelf = self { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift index c4d4e93fb84..b442a2b113c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -2043,7 +2043,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { for attribute in item.message.attributes { if let attribute = attribute as? ReplyMessageAttribute { return .optionalAction({ - item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote?.text : nil)) + item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote.flatMap { quote in NavigateToMessageParams.Quote(string: quote.text, offset: quote.offset) } : nil)) }) } else if let attribute = attribute as? ReplyStoryAttribute { return .optionalAction({ @@ -2072,7 +2072,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId, NavigateToMessageParams(timestamp: nil, quote: nil)) } else if let peer = forwardInfo.source ?? forwardInfo.author { - item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) + item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info(nil) : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) } else if let _ = forwardInfo.authorSignature { item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil) } @@ -2952,6 +2952,23 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { override public func contentFrame() -> CGRect { return self.imageNode.frame } + + override public func makeContentSnapshot() -> (UIImage, CGRect)? { + UIGraphicsBeginImageContextWithOptions(self.imageNode.view.bounds.size, false, 0.0) + let context = UIGraphicsGetCurrentContext()! + + context.translateBy(x: -self.imageNode.frame.minX, y: -self.imageNode.frame.minY) + self.view.layer.render(in: context) + + let image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + guard let image else { + return nil + } + + return (image, self.imageNode.frame) + } } public struct AnimatedEmojiSoundsConfiguration { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/BUILD index 4204788d7a4..8637dac9eb0 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/BUILD @@ -40,6 +40,9 @@ swift_library( "//submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentButtonNode", "//submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode", "//submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView", + "//submodules/ComponentFlow", + "//submodules/TelegramUI/Components/PlainButtonComponent", + "//submodules/Components/BundleIconComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift index 9e04fd426ed..919dfad20f0 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift @@ -29,6 +29,8 @@ import ChatMessageInteractiveMediaNode import WallpaperPreviewMedia import ChatMessageAttachedContentButtonNode import MessageInlineBlockBackgroundView +import ComponentFlow +import PlainButtonComponent public enum ChatMessageAttachedContentActionIcon { case instant @@ -67,6 +69,9 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { private var actionButtonSeparator: SimpleLayer? public var statusNode: ChatMessageDateAndStatusNode? + private var closeButton: ComponentView? + private var closeButtonImage: UIImage? + private var inlineMediaValue: Media? //private var additionalImageBadgeNode: ChatMessageInteractiveMediaBadge? @@ -88,6 +93,8 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { public var defaultContentAction: () -> ChatMessageBubbleContentTapAction = { return ChatMessageBubbleContentTapAction(content: .none) } + private var tapRecognizer: UITapGestureRecognizer? + public var visibility: ListViewItemNodeVisibility = .none { didSet { if oldValue != self.visibility { @@ -152,7 +159,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { let isPreview = presentationData.isPreview let fontSize: CGFloat if message.adAttribute != nil { - fontSize = floor(presentationData.fontSize.baseDisplaySize) + fontSize = floor(presentationData.fontSize.baseDisplaySize * 15.0 / 17.0) } else { fontSize = floor(presentationData.fontSize.baseDisplaySize * 14.0 / 17.0) } @@ -540,7 +547,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { actionTitle, mainColor, false, - message.adAttribute != nil + false ) actionButtonMinWidthAndFinalizeLayout = (buttonWidth, continueLayout) actualWidth = max(actualWidth, buttonWidth) @@ -606,47 +613,41 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { let maxStatusContentWidth: CGFloat = constrainedSize.width - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right var trailingContentWidth: CGFloat? - if let _ = message.adAttribute, let (textLayout, _) = textLayoutAndApply { - if textLayout.hasRTL { - trailingContentWidth = 10000.0 - } else { - trailingContentWidth = textLayout.trailingLineWidth - } - } else { - if !displayLine, let (actionButtonMinWidth, _) = actionButtonMinWidthAndFinalizeLayout { - trailingContentWidth = actionButtonMinWidth - } + if !displayLine, let (actionButtonMinWidth, _) = actionButtonMinWidthAndFinalizeLayout { + trailingContentWidth = actionButtonMinWidth } var statusLayoutAndContinue: (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation) -> ChatMessageDateAndStatusNode))? if case let .linear(_, bottom) = position { switch bottom { case .None, .Neighbour(_, .footer, _): - let statusLayoutAndContinueValue = makeStatusLayout(ChatMessageDateAndStatusNode.Arguments( - context: context, - presentationData: presentationData, - edited: edited, - impressionCount: viewCount, - dateText: dateText, - type: statusType, - layoutInput: .trailingContent( - contentWidth: trailingContentWidth, - reactionSettings: ChatMessageDateAndStatusNode.TrailingReactionSettings(displayInline: shouldDisplayInlineDateReactions(message: message, isPremium: associatedData.isPremium, forceInline: associatedData.forceInlineReactions), preferAdditionalInset: false) - ), - constrainedSize: CGSize(width: maxStatusContentWidth, height: CGFloat.greatestFiniteMagnitude), - availableReactions: associatedData.availableReactions, - reactions: dateReactionsAndPeers.reactions, - reactionPeers: dateReactionsAndPeers.peers, - displayAllReactionPeers: message.id.peerId.namespace == Namespaces.Peer.CloudUser, - replyCount: dateReplies, - isPinned: message.tags.contains(.pinned) && !associatedData.isInPinnedListMode && !isReplyThread, - hasAutoremove: message.isSelfExpiring, - canViewReactionList: canViewMessageReactionList(message: message), - animationCache: controllerInteraction.presentationContext.animationCache, - animationRenderer: controllerInteraction.presentationContext.animationRenderer - )) - statusLayoutAndContinue = statusLayoutAndContinueValue - actualWidth = max(actualWidth, statusLayoutAndContinueValue.0) + if message.adAttribute == nil { + let statusLayoutAndContinueValue = makeStatusLayout(ChatMessageDateAndStatusNode.Arguments( + context: context, + presentationData: presentationData, + edited: edited, + impressionCount: viewCount, + dateText: dateText, + type: statusType, + layoutInput: .trailingContent( + contentWidth: trailingContentWidth, + reactionSettings: ChatMessageDateAndStatusNode.TrailingReactionSettings(displayInline: shouldDisplayInlineDateReactions(message: message, isPremium: associatedData.isPremium, forceInline: associatedData.forceInlineReactions), preferAdditionalInset: false) + ), + constrainedSize: CGSize(width: maxStatusContentWidth, height: CGFloat.greatestFiniteMagnitude), + availableReactions: associatedData.availableReactions, + reactions: dateReactionsAndPeers.reactions, + reactionPeers: dateReactionsAndPeers.peers, + displayAllReactionPeers: message.id.peerId.namespace == Namespaces.Peer.CloudUser, + replyCount: dateReplies, + isPinned: message.tags.contains(.pinned) && !associatedData.isInPinnedListMode && !isReplyThread, + hasAutoremove: message.isSelfExpiring, + canViewReactionList: canViewMessageReactionList(message: message), + animationCache: controllerInteraction.presentationContext.animationCache, + animationRenderer: controllerInteraction.presentationContext.animationRenderer + )) + statusLayoutAndContinue = statusLayoutAndContinueValue + actualWidth = max(actualWidth, statusLayoutAndContinueValue.0) + } default: break } @@ -839,22 +840,22 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { } } - if case let .linear(_, bottom) = position, let statusSizeAndApply { + if case let .linear(_, bottom) = position { switch bottom { case .None, .Neighbour(_, .footer, _): - let bottomStatusContentHeight = statusBackgroundSpacing + statusSizeAndApply.0.height - actualSize.height += bottomStatusContentHeight - backgroundInsets.bottom += bottomStatusContentHeight + if let statusSizeAndApply { + let bottomStatusContentHeight = statusBackgroundSpacing + statusSizeAndApply.0.height + actualSize.height += bottomStatusContentHeight + backgroundInsets.bottom += bottomStatusContentHeight + } else { + actualSize.height += 11.0 + backgroundInsets.bottom += 11.0 + } default: break } } - if let _ = message.adAttribute { - actualSize.height += 2.0 - backgroundInsets.bottom += 2.0 - } - return (actualSize, { animation, synchronousLoads, applyInfo in guard let self else { return @@ -867,9 +868,9 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { animation.animator.updateFrame(layer: self.transformContainer.layer, frame: CGRect(origin: CGPoint(), size: actualSize), completion: nil) + let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top), size: CGSize(width: actualSize.width - backgroundInsets.left - backgroundInsets.right, height: actualSize.height - backgroundInsets.top - backgroundInsets.bottom)) + if displayLine { - let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top), size: CGSize(width: actualSize.width - backgroundInsets.left - backgroundInsets.right, height: actualSize.height - backgroundInsets.top - backgroundInsets.bottom)) - let backgroundView: MessageInlineBlockBackgroundView if let current = self.backgroundView { backgroundView = current @@ -982,11 +983,82 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { title.textNode.bounds = CGRect(origin: CGPoint(), size: titleFrame.size) animation.animator.updatePosition(layer: title.textNode.layer, position: titleFrame.origin, completion: nil) } + + if message.adAttribute != nil { + let closeButtonImage: UIImage + if let current = self.closeButtonImage { + closeButtonImage = current + } else { + closeButtonImage = generateImage(CGSize(width: 12.0, height: 12.0), rotatedContext: { size, context in + context.clear(CGRect(origin: .zero, size: size)) + + let color = UIColor.white + context.setAlpha(color.alpha) + context.setBlendMode(.copy) + + context.setStrokeColor(UIColor.white.cgColor) + context.setLineWidth(1.0 + UIScreenPixel) + context.setLineCap(.round) + + let bounds = CGRect(origin: .zero, size: size).insetBy(dx: 1.0 + UIScreenPixel, dy: 1.0 + UIScreenPixel) + + context.move(to: CGPoint(x: bounds.minX, y: bounds.minY)) + context.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY)) + context.strokePath() + + context.move(to: CGPoint(x: bounds.maxX, y: bounds.minY)) + context.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY)) + context.strokePath() + })!.withRenderingMode(.alwaysTemplate) + self.closeButtonImage = closeButtonImage + } + + let closeButton: ComponentView + if let current = self.closeButton { + closeButton = current + } else { + closeButton = ComponentView() + self.closeButton = closeButton + } + let closeButtonSize = closeButton.update( + transition: .immediate, + component: AnyComponent(PlainButtonComponent( + content: AnyComponent(Image(image: closeButtonImage, tintColor: mainColor)), + effectAlignment: .center, + action: { [weak controllerInteraction] in + guard let controllerInteraction else { + return + } + controllerInteraction.openNoAdsDemo() + } + )), + environment: {}, + containerSize: CGSize(width: 12.0, height: 12.0) + ) + + let closeButtonFrame = CGRect(origin: CGPoint(x: backgroundFrame.maxX - 8.0 - closeButtonSize.width, y: backgroundInsets.top + 8.0), size: closeButtonSize) + + if let closeButtonView = closeButton.view { + if closeButtonView.superview == nil { + self.transformContainer.view.addSubview(closeButtonView) + } + animation.animator.updateFrame(layer: closeButtonView.layer, frame: closeButtonFrame, completion: nil) + } + } else { + if let closeButton = self.closeButton { + self.closeButton = nil + closeButton.view?.removeFromSuperview() + } + } } else { if let title = self.title { self.title = nil title.textNode.removeFromSupernode() } + if let closeButton = self.closeButton { + self.closeButton = nil + closeButton.view?.removeFromSuperview() + } } if let item = contentDisplayOrder.first(where: { $0.item == .subtitle }), let (subtitleLayout, subtitleApply) = subtitleLayoutAndApply { @@ -1128,10 +1200,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { } if let item = contentDisplayOrder.first(where: { $0.item == .actionButton }), let (actionButtonSize, actionButtonApply) = actionButtonSizeAndApply { - var actionButtonFrame = CGRect(origin: CGPoint(x: insets.left, y: item.offsetY), size: actionButtonSize) - if let _ = message.adAttribute, let statusSizeAndApply { - actionButtonFrame.origin.y += statusSizeAndApply.0.height - } + let actionButtonFrame = CGRect(origin: CGPoint(x: insets.left, y: item.offsetY), size: actionButtonSize) let actionButton = actionButtonApply(animation) @@ -1150,25 +1219,21 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { } else { animation.animator.updateFrame(layer: actionButton.layer, frame: actionButtonFrame, completion: nil) } + + let separatorFrame = CGRect(origin: CGPoint(x: actionButtonFrame.minX, y: actionButtonFrame.minY - 1.0), size: CGSize(width: actionButtonFrame.width, height: UIScreenPixel)) - if let _ = message.adAttribute { - + let actionButtonSeparator: SimpleLayer + if let current = self.actionButtonSeparator { + actionButtonSeparator = current + animation.animator.updateFrame(layer: actionButtonSeparator, frame: separatorFrame, completion: nil) } else { - let separatorFrame = CGRect(origin: CGPoint(x: actionButtonFrame.minX, y: actionButtonFrame.minY - 1.0), size: CGSize(width: actionButtonFrame.width, height: UIScreenPixel)) - - let actionButtonSeparator: SimpleLayer - if let current = self.actionButtonSeparator { - actionButtonSeparator = current - animation.animator.updateFrame(layer: actionButtonSeparator, frame: separatorFrame, completion: nil) - } else { - actionButtonSeparator = SimpleLayer() - self.actionButtonSeparator = actionButtonSeparator - self.layer.addSublayer(actionButtonSeparator) - actionButtonSeparator.frame = separatorFrame - } - - actionButtonSeparator.backgroundColor = mainColor.withMultipliedAlpha(0.2).cgColor + actionButtonSeparator = SimpleLayer() + self.actionButtonSeparator = actionButtonSeparator + self.layer.addSublayer(actionButtonSeparator) + actionButtonSeparator.frame = separatorFrame } + + actionButtonSeparator.backgroundColor = mainColor.withMultipliedAlpha(0.2).cgColor } else { if let actionButton = self.actionButton { self.actionButton = nil @@ -1182,10 +1247,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { } if let statusSizeAndApply { - var statusFrame = CGRect(origin: CGPoint(x: actualSize.width - backgroundInsets.right - statusSizeAndApply.0.width, y: actualSize.height - layoutConstants.text.bubbleInsets.bottom - statusSizeAndApply.0.height), size: statusSizeAndApply.0) - if let _ = message.adAttribute, let (actionButtonSize, _) = actionButtonSizeAndApply { - statusFrame.origin.y -= actionButtonSize.height + statusBackgroundSpacing - } + let statusFrame = CGRect(origin: CGPoint(x: actualSize.width - backgroundInsets.right - statusSizeAndApply.0.width, y: actualSize.height - layoutConstants.text.bubbleInsets.bottom - statusSizeAndApply.0.height), size: statusSizeAndApply.0) let statusNode = statusSizeAndApply.1(self.statusNode == nil ? .None : animation) if self.statusNode !== statusNode { @@ -1216,12 +1278,36 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { self.statusNode = nil statusNode.removeFromSupernode() } + + if message.adAttribute != nil { + if self.tapRecognizer == nil { + let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))) + self.tapRecognizer = tapRecognizer + self.view.addGestureRecognizer(tapRecognizer) + } + } else { + if let tapRecognizer = self.tapRecognizer { + self.tapRecognizer = nil + self.view.removeGestureRecognizer(tapRecognizer) + } + } }) }) }) } } + @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { + guard let message = self.message else { + return + } + if case .ended = recognizer.state { + if message.adAttribute != nil { + self.activateAction?() + } + } + } + public func updateHiddenMedia(_ media: [Media]?) -> Bool { if let currentMedia = self.media { if let media = media { @@ -1296,9 +1382,13 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { } if let backgroundView = self.backgroundView, backgroundView.frame.contains(point) { - return self.defaultContentAction() + if let message = self.message, message.adAttribute != nil { + return ChatMessageBubbleContentTapAction(content: .none) + } else { + return self.defaultContentAction() + } } else { - return .init(content: .none) + return ChatMessageBubbleContentTapAction(content: .none) } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/BUILD index 5b8b444a8d7..bcba245fc1c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/BUILD @@ -79,6 +79,8 @@ swift_library( "//submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode", "//submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode", "//submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode", + "//submodules/TelegramUI/Components/Chat/ChatMessageJoinedChannelBubbleContentNode", + "//submodules/UIKitRuntimeUtils", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 2df8623561d..278e6603cb1 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -88,6 +88,8 @@ import ChatMessageUnsupportedBubbleContentNode import ChatMessageWallpaperBubbleContentNode import ChatMessageGiftBubbleContentNode import ChatMessageGiveawayBubbleContentNode +import ChatMessageJoinedChannelBubbleContentNode +import UIKitRuntimeUtils private struct BubbleItemAttributes { var isAttachment: Bool @@ -206,6 +208,8 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([ result.append((message, ChatMessageWallpaperBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default))) } else if case .giftCode = action.action { result.append((message, ChatMessageGiftBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default))) + } else if case .joinedChannel = action.action { + result.append((message, ChatMessageJoinedChannelBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default))) } else { result.append((message, ChatMessageActionBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default))) } @@ -595,7 +599,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI private var backgroundType: ChatMessageBackgroundType? private struct HighlightedState: Equatable { - var quote: String? + var quote: ChatInterfaceHighlightedState.Quote? } private var highlightedState: HighlightedState? @@ -1383,7 +1387,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI ignoreForward = true effectiveAuthor = forwardInfo.author if effectiveAuthor == nil, let authorSignature = forwardInfo.authorSignature { - effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(authorSignature.persistentHashValue % 32))), accessHash: nil, firstName: authorSignature, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil) + effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(authorSignature.persistentHashValue % 32))), accessHash: nil, firstName: authorSignature, 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) } } displayAuthorInfo = !mergedTop.merged && incoming && effectiveAuthor != nil @@ -1399,7 +1403,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI displayAuthorInfo = !mergedTop.merged && incoming } else if let forwardInfo = item.content.firstMessage.forwardInfo, forwardInfo.flags.contains(.isImported), let authorSignature = forwardInfo.authorSignature { ignoreForward = true - effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(authorSignature.persistentHashValue % 32))), accessHash: nil, firstName: authorSignature, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil) + effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(authorSignature.persistentHashValue % 32))), accessHash: nil, firstName: authorSignature, 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) displayAuthorInfo = !mergedTop.merged && incoming } else if let adAttribute = item.content.firstMessage.adAttribute, let author = item.content.firstMessage.author { ignoreForward = true @@ -1639,6 +1643,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI var hasInstantVideo = false for contentNodeItemValue in contentNodeMessagesAndClasses { let contentNodeItem = contentNodeItemValue as (message: Message, type: AnyClass, attributes: ChatMessageEntryAttributes, bubbleAttributes: BubbleItemAttributes) + if contentNodeItem.type == ChatMessageJoinedChannelBubbleContentNode.self { + maximumContentWidth = baseWidth + break + } if contentNodeItem.type == ChatMessageGiveawayBubbleContentNode.self { maximumContentWidth = min(305.0, maximumContentWidth) break @@ -1961,7 +1969,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI var currentCredibilityIcon: EmojiStatusComponent.Content? var initialDisplayHeader = true - if hidesHeaders { + if hidesHeaders || item.message.adAttribute != nil { initialDisplayHeader = false } else if let backgroundHiding, case .always = backgroundHiding { initialDisplayHeader = false @@ -2236,6 +2244,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI let bodyAttributes = MarkdownAttributeSet(font: inlineBotPrefixFont, textColor: inlineBotNameColor) let boldAttributes = MarkdownAttributeSet(font: nameFont, textColor: inlineBotNameColor) attributedString = addAttributesToStringWithRanges(item.presentationData.strings.Conversation_MessageViaUser("@\(inlineBotNameString)")._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes]) + viaSuffix = attributedString } else { attributedString = NSAttributedString(string: "", font: nameFont, textColor: inlineBotNameColor) } @@ -4191,17 +4200,27 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI strongSelf.mainContextSourceNode.layoutUpdated?(strongSelf.mainContextSourceNode.bounds.size, animation) } + var hasMenuGesture = true if let subject = item.associatedData.subject, case let .messageOptions(_, _, info) = subject { if case .link = info { } else { strongSelf.tapRecognizer?.isEnabled = false } strongSelf.replyRecognizer?.isEnabled = false - strongSelf.mainContainerNode.isGestureEnabled = false - for contentContainer in strongSelf.contentContainers { - contentContainer.containerNode.isGestureEnabled = false + hasMenuGesture = false + } + for media in item.message.media { + if let action = media as? TelegramMediaAction { + if case .joinedChannel = action.action { + hasMenuGesture = false + break + } } } + strongSelf.mainContainerNode.isGestureEnabled = hasMenuGesture + for contentContainer in strongSelf.contentContainers { + contentContainer.containerNode.isGestureEnabled = hasMenuGesture + } strongSelf.updateSearchTextHighlightState() @@ -4413,7 +4432,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if let replyInfoNode = self.replyInfoNode { progress = replyInfoNode.makeProgress() } - item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote?.text : nil, progress: progress)) + item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote.flatMap { quote in NavigateToMessageParams.Quote(string: quote.text, offset: quote.offset) } : nil, progress: progress)) }, contextMenuOnLongPress: true)) } else if let attribute = attribute as? ReplyStoryAttribute { return .action(InternalBubbleTapAction.Action({ @@ -4461,7 +4480,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId, NavigateToMessageParams(timestamp: nil, quote: nil)) } else if let peer = forwardInfo.source ?? forwardInfo.author { - item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) + item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info(nil) : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) } else if let _ = forwardInfo.authorSignature { item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil) } @@ -4481,7 +4500,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } else { if let peer = item.message.peers[story.storyId.peerId] { return .action(InternalBubbleTapAction.Action { - item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) + item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info(nil) : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) }) } } @@ -4535,7 +4554,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI let _ = (item.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).startStandalone(next: { peer in if let self = self, let item = self.item, let peer = peer { - item.controllerInteraction.openPeer(peer, openProfile ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) + item.controllerInteraction.openPeer(peer, openProfile ? .info(nil) : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) } }) } @@ -5063,7 +5082,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI for contentNode in self.contentNodes { if let contentNode = contentNode as? ChatMessageTextBubbleContentNode { - contentNode.updateQuoteTextHighlightState(text: nil, color: .clear, animated: true) + contentNode.updateQuoteTextHighlightState(text: nil, offset: nil, color: .clear, animated: true) } } @@ -5116,7 +5135,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI var quoteFrame: CGRect? for contentNode in self.contentNodes { if let contentNode = contentNode as? ChatMessageTextBubbleContentNode { - contentNode.updateQuoteTextHighlightState(text: quote, color: highlightColor, animated: false) + contentNode.updateQuoteTextHighlightState(text: quote.string, offset: quote.offset, color: highlightColor, animated: false) var sourceFrame = backgroundHighlightNode.view.convert(backgroundHighlightNode.bounds, to: contentNode.view) if item.message.effectivelyIncoming(item.context.account.peerId) { sourceFrame.origin.x += 6.0 @@ -5255,7 +5274,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if let channel = peer as? TelegramChannel, case .broadcast = channel.info { item.controllerInteraction.openPeer(EnginePeer(peer), .chat(textInputState: nil, subject: nil, peekData: nil), messageReference, .default) } else { - item.controllerInteraction.openPeer(EnginePeer(peer), .info, messageReference, .groupParticipant(storyStats: nil, avatarHeaderNode: nil)) + item.controllerInteraction.openPeer(EnginePeer(peer), .info(nil), messageReference, .groupParticipant(storyStats: nil, avatarHeaderNode: nil)) } } } @@ -5630,10 +5649,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI return nil } - public func getQuoteRect(quote: String) -> CGRect? { + public func getQuoteRect(quote: String, offset: Int?) -> CGRect? { for contentNode in self.contentNodes { if let contentNode = contentNode as? ChatMessageTextBubbleContentNode { - if let result = contentNode.getQuoteRect(quote: quote) { + if let result = contentNode.getQuoteRect(quote: quote, offset: offset) { return contentNode.view.convert(result, to: self.view) } } @@ -5655,4 +5674,75 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI override public func contentFrame() -> CGRect { return self.backgroundNode.frame } + + override public func makeContentSnapshot() -> (UIImage, CGRect)? { + UIGraphicsBeginImageContextWithOptions(self.backgroundNode.view.bounds.size, false, 0.0) + let context = UIGraphicsGetCurrentContext()! + + context.translateBy(x: -self.backgroundNode.frame.minX, y: -self.backgroundNode.frame.minY) + + context.translateBy(x: -self.mainContextSourceNode.contentNode.view.frame.minX, y: -self.mainContextSourceNode.contentNode.view.frame.minY) + for subview in self.mainContextSourceNode.contentNode.view.subviews { + if subview.isHidden || subview.alpha == 0.0 { + continue + } + if subview === self.backgroundWallpaperNode.view { + var targetPortalView: UIView? + for backgroundSubview0 in subview.subviews { + for backgroundSubview1 in backgroundSubview0.subviews { + if isViewPortalView(backgroundSubview1) { + targetPortalView = backgroundSubview1 + break + } + } + } + + if let targetPortalView, let sourceView = getPortalViewSourceView(targetPortalView) { + context.saveGState() + context.translateBy(x: subview.frame.minX, y: subview.frame.minY) + + if let mask = subview.mask { + let maskImage = generateImage(subview.bounds.size, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + UIGraphicsPushContext(context) + mask.drawHierarchy(in: mask.frame, afterScreenUpdates: false) + UIGraphicsPopContext() + }) + if let cgImage = maskImage?.cgImage { + context.translateBy(x: subview.frame.midX, y: subview.frame.midY) + context.scaleBy(x: 1.0, y: -1.0) + context.translateBy(x: -subview.frame.midX, y: -subview.frame.midY) + + context.clip(to: subview.bounds, mask: cgImage) + + context.translateBy(x: subview.frame.midX, y: subview.frame.midY) + context.scaleBy(x: 1.0, y: -1.0) + context.translateBy(x: -subview.frame.midX, y: -subview.frame.midY) + } + } + + let sourceLocalFrame = sourceView.convert(sourceView.bounds, to: subview) + for sourceSubview in sourceView.subviews { + sourceSubview.drawHierarchy(in: CGRect(origin: sourceLocalFrame.origin, size: sourceSubview.bounds.size), afterScreenUpdates: false) + } + + context.resetClip() + context.restoreGState() + } else { + subview.drawHierarchy(in: subview.frame, afterScreenUpdates: false) + } + } else { + subview.drawHierarchy(in: subview.frame, afterScreenUpdates: false) + } + } + + let image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + guard let image else { + return nil + } + + return (image, self.backgroundNode.frame) + } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift index 717ae063e91..0b9cee1b0ab 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift @@ -1373,5 +1373,5 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { } public func shouldDisplayInlineDateReactions(message: Message, isPremium: Bool, forceInline: Bool) -> Bool { - return forceInline + return false } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift index 435105c17aa..5b25714ce70 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift @@ -310,7 +310,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { let (buttonTitleLayout, buttonTitleApply) = makeButtonTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: buttonTitle, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: giftSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets())) - giftSize.height = titleLayout.size.height + subtitleLayout.size.height + 225.0 + giftSize.height = titleLayout.size.height + textSpacing + subtitleLayout.size.height + 212.0 var labelRects = labelLayout.linesRects() if labelRects.count > 1 { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift index d65462b819c..3222c8b419c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift @@ -684,7 +684,7 @@ private final class PeerButtonsStackNode: ASDisplayNode { var titleColor = titleColor var backgroundColor = backgroundColor - if incoming, let nameColor = peer.nameColor { + if incoming, let nameColor = peer.nameColor, makeChannelButtonLayouts.count > 1 { titleColor = context.peerNameColors.get(nameColor, dark: dark).main backgroundColor = titleColor.withAlphaComponent(0.1) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift index 92dc14d03c5..e0e3ebe07f2 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift @@ -963,7 +963,7 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureReco for attribute in item.message.attributes { if let attribute = attribute as? ReplyMessageAttribute { return .optionalAction({ - item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote?.text : nil)) + item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote.flatMap { quote in NavigateToMessageParams.Quote(string: quote.text, offset: quote.offset) } : nil)) }) } else if let attribute = attribute as? QuotedReplyMessageAttribute { return .action(InternalBubbleTapAction.Action { @@ -988,7 +988,7 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureReco } item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId, NavigateToMessageParams(timestamp: nil, quote: nil)) } else if let peer = forwardInfo.source ?? forwardInfo.author { - item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) + item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info(nil) : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) } else if let _ = forwardInfo.authorSignature { item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift index e20a180534d..164ad096c67 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift @@ -40,6 +40,7 @@ import ChatControllerInteraction import ChatMessageDateAndStatusNode import ChatHistoryEntry import ChatMessageItemCommon +import TelegramStringFormatting private struct FetchControls { let fetch: (Bool) -> Void @@ -342,42 +343,63 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { } private func transcribe() { - guard let arguments = self.arguments, let context = self.context, let message = self.message, let presentationData = self.presentationData else { + guard let arguments = self.arguments, let context = self.context, let message = self.message else { return } - // MARK: Nicegram Speech2Text, nicegram premium check + // MARK: Nicegram Speech2Text let isNicegramPremium = isPremium() - guard arguments.associatedData.isPremium || isNicegramPremium else { - // MARK: Nicegram Speech2Text, routeToNicegramPremium - if isNicegram() { - PremiumUITgHelper.routeToPremium() - return - } - // - - if self.hapticFeedback == nil { - self.hapticFeedback = HapticFeedback() - } - self.hapticFeedback?.impact(.medium) - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let tipController = UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_voiceToText", scale: 0.065, colors: [:], title: nil, text: presentationData.strings.Message_AudioTranscription_SubscribeToPremium, customUndoText: presentationData.strings.Message_AudioTranscription_SubscribeToPremiumAction, timeout: nil), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { action in - if case .undo = action { - var replaceImpl: ((ViewController) -> Void)? - let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, action: { - let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false, dismissed: nil) - replaceImpl?(controller) - }) - replaceImpl = { [weak controller] c in - controller?.replace(with: c) + // + + if !context.isPremium, case .inProgress = self.audioTranscriptionState { + return + } + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: arguments.context.currentAppConfiguration.with { $0 }) + + let transcriptionText = self.forcedAudioTranscriptionText ?? transcribedText(message: message) + if transcriptionText == nil { + if premiumConfiguration.audioTransciptionTrialCount > 0 { + if !arguments.associatedData.isPremium { + if self.presentAudioTranscriptionTooltip(finished: false) { + return } - arguments.controllerInteraction.navigationController()?.pushViewController(controller, animated: true) + } + } else { + // MARK: Nicegram Speech2Text, nicegram premium check + guard arguments.associatedData.isPremium || isNicegramPremium else { + // MARK: Nicegram Speech2Text, routeToNicegramPremium + if isNicegram() { + PremiumUITgHelper.routeToPremium() + return + } + // - let _ = ApplicationSpecificNotice.incrementAudioTranscriptionSuggestion(accountManager: context.sharedContext.accountManager).startStandalone() + if self.hapticFeedback == nil { + self.hapticFeedback = HapticFeedback() + } + self.hapticFeedback?.impact(.medium) + + let tipController = UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_voiceToText", scale: 0.065, colors: [:], title: nil, text: presentationData.strings.Message_AudioTranscription_SubscribeToPremium, customUndoText: presentationData.strings.Message_AudioTranscription_SubscribeToPremiumAction, timeout: nil), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { action in + if case .undo = action { + var replaceImpl: ((ViewController) -> Void)? + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, action: { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false, dismissed: nil) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + arguments.controllerInteraction.navigationController()?.pushViewController(controller, animated: true) + + let _ = ApplicationSpecificNotice.incrementAudioTranscriptionSuggestion(accountManager: context.sharedContext.accountManager).startStandalone() + } + return false }) + arguments.controllerInteraction.presentControllerInCurrent(tipController, nil) + return } - return false }) - arguments.controllerInteraction.presentControllerInCurrent(tipController, nil) - return + } } var shouldBeginTranscription = false @@ -506,6 +528,12 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { // strongSelf.transcribeDisposable?.dispose() strongSelf.transcribeDisposable = nil + + if let arguments = strongSelf.arguments, !arguments.associatedData.isPremium { + Queue.mainQueue().after(0.1, { + let _ = strongSelf.presentAudioTranscriptionTooltip(finished: true) + }) + } }) } } @@ -527,6 +555,58 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { } } + private func presentAudioTranscriptionTooltip(finished: Bool) -> Bool { + guard let arguments = self.arguments, !arguments.associatedData.isPremium else { + return false + } + + let presentationData = arguments.context.sharedContext.currentPresentationData.with { $0 } + var text: String? + var timeout: Double = 5.0 + + let currentTime = Int32(Date().timeIntervalSince1970) + if let cooldownUntilTime = arguments.associatedData.audioTranscriptionTrial.cooldownUntilTime, cooldownUntilTime > currentTime { + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: arguments.context.currentAppConfiguration.with { $0 }) + + let time = stringForMediumDate(timestamp: cooldownUntilTime, strings: arguments.presentationData.strings, dateTimeFormat: arguments.presentationData.dateTimeFormat) + let usedString = arguments.presentationData.strings.Conversation_FreeTranscriptionCooldownTooltip(premiumConfiguration.audioTransciptionTrialCount) + let waitString = arguments.presentationData.strings.Conversation_FreeTranscriptionWaitOrSubscribe(time).string + let fullString = "\(usedString) \(waitString)" + text = fullString + + if self.hapticFeedback == nil { + self.hapticFeedback = HapticFeedback() + } + self.hapticFeedback?.impact(.medium) + timeout = 7.0 + } else if finished { + let remainingCount = arguments.associatedData.audioTranscriptionTrial.remainingCount + text = arguments.presentationData.strings.Conversation_FreeTranscriptionLimitTooltip(remainingCount) + } + + guard let text else { + return false + } + let context = arguments.context + let tipController = UndoOverlayController(presentationData: presentationData, content: .universal(animation: "Transcribe", scale: 0.06, colors: [:], title: nil, text: text, customUndoText: nil, timeout: timeout), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { action in + if case .info = action { + var replaceImpl: ((ViewController) -> Void)? + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, action: { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false, dismissed: nil) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + arguments.controllerInteraction.navigationController()?.pushViewController(controller, animated: true) + return true + } + return false + }) + arguments.controllerInteraction.presentControllerInCurrent(tipController, nil) + return true + } + public func asyncLayout() -> (Arguments) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool, ListViewItemUpdateAnimation, ListViewItemApply?) -> Void))) { let currentFile = self.file @@ -726,24 +806,25 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { var textString: NSAttributedString? var updatedAudioTranscriptionState: AudioTranscriptionButtonComponent.TranscriptionState? - let displayTranscribe: Bool + var displayTranscribe = false if arguments.message.id.peerId.namespace != Namespaces.Peer.SecretChat { + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: arguments.context.currentAppConfiguration.with { $0 }) // MARK: Nicegram Speech2Text, nicegram premium check if arguments.associatedData.isPremium || isPremium() { displayTranscribe = true + } else if premiumConfiguration.audioTransciptionTrialCount > 0 { + if arguments.incoming { + if audioDuration < premiumConfiguration.audioTransciptionTrialMaxDuration { + displayTranscribe = true + } + } } else if arguments.associatedData.alwaysDisplayTranscribeButton.canBeDisplayed { if audioDuration >= 60 { displayTranscribe = true } else if arguments.incoming && isConsumed == false && arguments.associatedData.alwaysDisplayTranscribeButton.displayForNotConsumed { displayTranscribe = true - } else { - displayTranscribe = false } - } else { - displayTranscribe = false } - } else { - displayTranscribe = false } let transcribedText = forcedAudioTranscriptionText ?? transcribedText(message: arguments.message) @@ -758,6 +839,11 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { break } + let currentTime = Int32(Date().timeIntervalSince1970) + if transcribedText == nil, let cooldownUntilTime = arguments.associatedData.audioTranscriptionTrial.cooldownUntilTime, cooldownUntilTime > currentTime { + updatedAudioTranscriptionState = .locked + } + let effectiveAudioTranscriptionState = updatedAudioTranscriptionState ?? audioTranscriptionState var displayTrailingAnimatedDots = false diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift index f0aa5172149..9f949769df1 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -28,6 +28,7 @@ import InstantVideoRadialStatusNode import ChatInstantVideoMessageDurationNode import ChatControllerInteraction import WallpaperBackgroundNode +import TelegramStringFormatting public struct ChatMessageInstantVideoItemLayoutResult { public let contentSize: CGSize @@ -590,6 +591,11 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { updatedTranscriptionText = transcribedText } + let currentTime = Int32(Date().timeIntervalSince1970) + if transcribedText == nil, let cooldownUntilTime = item.associatedData.audioTranscriptionTrial.cooldownUntilTime, cooldownUntilTime > currentTime { + updatedAudioTranscriptionState = .locked + } + let effectiveAudioTranscriptionState = updatedAudioTranscriptionState ?? audioTranscriptionState return (result, { [weak self] layoutData, animation in @@ -775,21 +781,22 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { })) } - var displayTranscribe: Bool + var displayTranscribe = false if item.message.id.peerId.namespace != Namespaces.Peer.SecretChat && statusDisplayType == .free { + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 }) if item.associatedData.isPremium { displayTranscribe = true + } else if premiumConfiguration.audioTransciptionTrialCount > 0 { + if incoming { + displayTranscribe = true + } } else if item.associatedData.alwaysDisplayTranscribeButton.canBeDisplayed { if incoming && notConsumed && item.associatedData.alwaysDisplayTranscribeButton.displayForNotConsumed { displayTranscribe = true } else { displayTranscribe = false } - } else { - displayTranscribe = false } - } else { - displayTranscribe = false } if displayTranscribe, let durationBlurColor = durationBlurColor { @@ -1345,7 +1352,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { if let item = self.item { for attribute in item.message.attributes { if let attribute = attribute as? ReplyMessageAttribute { - item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote?.text : nil)) + item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote.flatMap { quote in NavigateToMessageParams.Quote(string: quote.text, offset: quote.offset) } : nil)) return } else if let attribute = attribute as? ReplyStoryAttribute { item.controllerInteraction.navigateToStory(item.message, attribute.storyId) @@ -1372,7 +1379,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId, NavigateToMessageParams(timestamp: nil, quote: nil)) return } else if let peer = forwardInfo.source ?? forwardInfo.author { - item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) + item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info(nil) : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) return } else if let _ = forwardInfo.authorSignature { item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil) @@ -1614,32 +1621,50 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { guard let item = self.item, item.message.id.namespace == Namespaces.Message.Cloud else { return } - - guard item.associatedData.isPremium else { - if self.hapticFeedback == nil { - self.hapticFeedback = HapticFeedback() - } - self.hapticFeedback?.impact(.medium) - - let presentationData = item.context.sharedContext.currentPresentationData.with { $0 } - let tipController = UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_voiceToText", scale: 0.065, colors: [:], title: nil, text: presentationData.strings.Message_AudioTranscription_SubscribeToPremium, customUndoText: presentationData.strings.Message_AudioTranscription_SubscribeToPremiumAction, timeout: nil), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { action in - if case .undo = action { - let context = item.context - var replaceImpl: ((ViewController) -> Void)? - let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, action: { - let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false, dismissed: nil) - replaceImpl?(controller) - }) - replaceImpl = { [weak controller] c in - controller?.replace(with: c) + + if !item.context.isPremium, case .inProgress = self.audioTranscriptionState { + return + } + + let presentationData = item.context.sharedContext.currentPresentationData.with { $0 } + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 }) + + let transcriptionText = transcribedText(message: item.message) + if transcriptionText == nil { + if premiumConfiguration.audioTransciptionTrialCount > 0 { + if !item.associatedData.isPremium { + if self.presentAudioTranscriptionTooltip(finished: false) { + return } - item.controllerInteraction.navigationController()?.pushViewController(controller, animated: true) + } + } else { + guard item.associatedData.isPremium else { + if self.hapticFeedback == nil { + self.hapticFeedback = HapticFeedback() + } + self.hapticFeedback?.impact(.medium) - let _ = ApplicationSpecificNotice.incrementAudioTranscriptionSuggestion(accountManager: item.context.sharedContext.accountManager).startStandalone() + + let tipController = UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_voiceToText", scale: 0.065, colors: [:], title: nil, text: presentationData.strings.Message_AudioTranscription_SubscribeToPremium, customUndoText: presentationData.strings.Message_AudioTranscription_SubscribeToPremiumAction, timeout: nil), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { action in + if case .undo = action { + let context = item.context + var replaceImpl: ((ViewController) -> Void)? + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, action: { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false, dismissed: nil) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + item.controllerInteraction.navigationController()?.pushViewController(controller, animated: true) + + let _ = ApplicationSpecificNotice.incrementAudioTranscriptionSuggestion(accountManager: item.context.sharedContext.accountManager).startStandalone() + } + return false }) + item.controllerInteraction.presentControllerInCurrent(tipController, nil) + return } - return false }) - item.controllerInteraction.presentControllerInCurrent(tipController, nil) - return + } } var shouldBeginTranscription = false @@ -1673,6 +1698,12 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } strongSelf.transcribeDisposable?.dispose() strongSelf.transcribeDisposable = nil + + if let item = strongSelf.item, !item.associatedData.isPremium { + Queue.mainQueue().after(0.1, { + let _ = strongSelf.presentAudioTranscriptionTooltip(finished: true) + }) + } }) } } @@ -1694,6 +1725,58 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { self.updateTranscriptionExpanded?(self.audioTranscriptionState) } + private func presentAudioTranscriptionTooltip(finished: Bool) -> Bool { + guard let item = self.item, !item.associatedData.isPremium else { + return false + } + + let presentationData = item.context.sharedContext.currentPresentationData.with { $0 } + var text: String? + var timeout: Double = 5.0 + + let currentTime = Int32(Date().timeIntervalSince1970) + if let cooldownUntilTime = item.associatedData.audioTranscriptionTrial.cooldownUntilTime, cooldownUntilTime > currentTime { + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 }) + + let time = stringForMediumDate(timestamp: cooldownUntilTime, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat) + let usedString = presentationData.strings.Conversation_FreeTranscriptionCooldownTooltip(premiumConfiguration.audioTransciptionTrialCount) + let waitString = presentationData.strings.Conversation_FreeTranscriptionWaitOrSubscribe(time).string + let fullString = "\(usedString) \(waitString)" + text = fullString + + if self.hapticFeedback == nil { + self.hapticFeedback = HapticFeedback() + } + self.hapticFeedback?.impact(.medium) + timeout = 7.0 + } else if finished { + let remainingCount = item.associatedData.audioTranscriptionTrial.remainingCount + text = presentationData.strings.Conversation_FreeTranscriptionLimitTooltip(remainingCount) + } + + guard let text else { + return false + } + let context = item.context + let tipController = UndoOverlayController(presentationData: presentationData, content: .universal(animation: "Transcribe", scale: 0.06, colors: [:], title: nil, text: text, customUndoText: nil, timeout: timeout), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { action in + if case .info = action { + var replaceImpl: ((ViewController) -> Void)? + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, action: { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false, dismissed: nil) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + item.controllerInteraction.navigationController()?.pushViewController(controller, animated: true) + return true + } + return false + }) + item.controllerInteraction.presentControllerInCurrent(tipController, nil) + return true + } + public final class AnimateFileNodeDescription { public let node: ASDisplayNode public let textClippingNode: ASDisplayNode diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift index 2be99311662..dfaf277534e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift @@ -1579,7 +1579,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr } if let updateImageSignal = updateImageSignal { - strongSelf.imageNode.captureProtected = message.id.peerId.namespace == Namespaces.Peer.SecretChat || message.isCopyProtected() || isExtendedMedia + strongSelf.imageNode.captureProtected = message.isCopyProtected() || isExtendedMedia strongSelf.imageNode.setSignal(updateImageSignal(synchronousLoads, false), attemptSynchronously: synchronousLoads) var imageDimensions: CGSize? diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift index 1f9dc254729..f04cc064961 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift @@ -735,7 +735,7 @@ public final class ChatMessageAvatarHeaderNodeImpl: ListViewItemHeaderNode, Chat if let channel = peer as? TelegramChannel, case .broadcast = channel.info { self.controllerInteraction.openPeer(EnginePeer(peer), .chat(textInputState: nil, subject: nil, peekData: nil), self.messageReference, .default) } else { - self.controllerInteraction.openPeer(EnginePeer(peer), .info, self.messageReference, .groupParticipant(storyStats: nil, avatarHeaderNode: self)) + self.controllerInteraction.openPeer(EnginePeer(peer), .info(nil), self.messageReference, .groupParticipant(storyStats: nil, avatarHeaderNode: self)) } } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift index be55f582a56..f64cfb2dff5 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift @@ -261,7 +261,7 @@ public final class ChatMessageItemImpl: ChatMessageItem, CustomStringConvertible if let forwardInfo = content.firstMessage.forwardInfo { effectiveAuthor = forwardInfo.author if effectiveAuthor == nil, let authorSignature = forwardInfo.authorSignature { - effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(authorSignature.persistentHashValue % 32))), accessHash: nil, firstName: authorSignature, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil) + effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(authorSignature.persistentHashValue % 32))), accessHash: nil, firstName: authorSignature, 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) } } displayAuthorInfo = incoming && effectiveAuthor != nil diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemView/Sources/ChatMessageItemView.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemView/Sources/ChatMessageItemView.swift index 1eb561e3961..0ba0e07ddf7 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemView/Sources/ChatMessageItemView.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemView/Sources/ChatMessageItemView.swift @@ -720,6 +720,10 @@ open class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol { return nil } + open func makeContentSnapshot() -> (UIImage, CGRect)? { + return nil + } + open func getMessageContextSourceNode(stableId: UInt32?) -> ContextExtractedContentContainingNode? { return nil } @@ -823,7 +827,7 @@ open class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol { let _ = (item.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).startStandalone(next: { peer in if let peer = peer { - item.controllerInteraction.openPeer(peer, .info, nil, .default) + item.controllerInteraction.openPeer(peer, .info(nil), nil, .default) } }) case let .openWebView(url, simple): diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageJoinedChannelBubbleContentNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageJoinedChannelBubbleContentNode/BUILD new file mode 100644 index 00000000000..9f0577a6505 --- /dev/null +++ b/submodules/TelegramUI/Components/Chat/ChatMessageJoinedChannelBubbleContentNode/BUILD @@ -0,0 +1,43 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ChatMessageJoinedChannelBubbleContentNode", + module_name = "ChatMessageJoinedChannelBubbleContentNode", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/TelegramCore", + "//submodules/AccountContext", + "//submodules/ComponentFlow", + "//submodules/TelegramPresentationData", + "//submodules/TelegramUIPreferences", + "//submodules/TextFormat", + "//submodules/LocalizedPeerData", + "//submodules/UrlEscaping", + "//submodules/TelegramStringFormatting", + "//submodules/WallpaperBackgroundNode", + "//submodules/TelegramUI/Components/ChatControllerInteraction", + "//submodules/ShimmerEffect", + "//submodules/Markdown", + "//submodules/AvatarNode", + "//submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode", + "//submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode", + "//submodules/TelegramUI/Components/Chat/ChatMessageItemCommon", + "//submodules/TelegramUI/Components/Utils/RoundedRectWithTailPath", + "//submodules/Components/MultilineTextComponent", + "//submodules/Components/BundleIconComponent", + "//submodules/ChatMessageBackground", + "//submodules/ContextUI", + "//submodules/UndoUI", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageJoinedChannelBubbleContentNode/Sources/ChatMessageJoinedChannelBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageJoinedChannelBubbleContentNode/Sources/ChatMessageJoinedChannelBubbleContentNode.swift new file mode 100644 index 00000000000..a8bb5342c67 --- /dev/null +++ b/submodules/TelegramUI/Components/Chat/ChatMessageJoinedChannelBubbleContentNode/Sources/ChatMessageJoinedChannelBubbleContentNode.swift @@ -0,0 +1,1164 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import ComponentFlow +import SwiftSignalKit +import TelegramCore +import AccountContext +import TelegramPresentationData +import TelegramUIPreferences +import TextFormat +import LocalizedPeerData +import UrlEscaping +import TelegramStringFormatting +import WallpaperBackgroundNode +import ReactionSelectionNode +import ChatControllerInteraction +import ShimmerEffect +import Markdown +import ChatMessageBubbleContentNode +import ChatMessagePollBubbleContentNode +import ChatMessageItemCommon +import RoundedRectWithTailPath +import AvatarNode +import MultilineTextComponent +import BundleIconComponent +import ChatMessageBackground +import ContextUI +import UndoUI + +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) +} + +private func generateCloseButtonImage(color: UIColor) -> UIImage? { + return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + context.setAlpha(color.alpha) + context.setBlendMode(.copy) + + context.setLineWidth(2.0) + context.setLineCap(.round) + context.setStrokeColor(color.withAlphaComponent(1.0).cgColor) + + context.move(to: CGPoint(x: 10.0, y: 10.0)) + context.addLine(to: CGPoint(x: 20.0, y: 20.0)) + context.strokePath() + + context.move(to: CGPoint(x: 20.0, y: 10.0)) + context.addLine(to: CGPoint(x: 10.0, y: 20.0)) + context.strokePath() + }) +} + +public class ChatMessageJoinedChannelBubbleContentNode: ChatMessageBubbleContentNode { + private let labelNode: TextNode + private var backgroundNode: WallpaperBubbleBackgroundNode? + private let backgroundMaskNode: ASImageNode + private var linkHighlightingNode: LinkHighlightingNode? + + private let panelNode: ASDisplayNode + private let panelBackgroundNode: MessageBackgroundNode + private let titleNode: TextNode + private let closeButtonNode: HighlightTrackingButtonNode + private let closeIconNode: ASImageNode + private let panelListView = ComponentView() + + private var cachedMaskBackgroundImage: (CGPoint, UIImage, [CGRect])? + private var absoluteRect: (CGRect, CGSize)? + + private var currentMaskSize: CGSize? + private var panelMaskLayer: CAShapeLayer? + + private var isExpanded: Bool? + + required public init() { + self.labelNode = TextNode() + self.labelNode.isUserInteractionEnabled = false + self.labelNode.displaysAsynchronously = false + + self.backgroundMaskNode = ASImageNode() + + self.panelNode = ASDisplayNode() + self.panelBackgroundNode = MessageBackgroundNode() + + self.titleNode = TextNode() + self.titleNode.isUserInteractionEnabled = false + self.titleNode.displaysAsynchronously = false + + self.closeButtonNode = HighlightTrackingButtonNode() + + self.closeIconNode = ASImageNode() + self.closeIconNode.displaysAsynchronously = false + self.closeIconNode.isUserInteractionEnabled = false + + super.init() + + self.addSubnode(self.labelNode) + + self.panelNode.anchorPoint = CGPoint(x: 0.5, y: -0.1) + + self.addSubnode(self.panelNode) + self.panelNode.addSubnode(self.panelBackgroundNode) + self.panelNode.addSubnode(self.titleNode) + + self.panelNode.addSubnode(self.closeIconNode) + self.panelNode.addSubnode(self.closeButtonNode) + + self.closeButtonNode.highligthedChanged = { [weak self] highlighted in + guard let self else { + return + } + if highlighted { + self.closeIconNode.layer.removeAnimation(forKey: "opacity") + self.closeIconNode.alpha = 0.4 + } else { + self.closeIconNode.alpha = 1.0 + self.closeIconNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + } + } + self.closeButtonNode.addTarget(self, action: #selector(self.closeButtonPressed), forControlEvents: .touchUpInside) + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public override func didLoad() { + super.didLoad() + + self.panelMaskLayer = CAShapeLayer() + } + + @objc private func pressed() { + guard let item = self.item else { + return + } + if let recommendedChannels = item.associatedData.recommendedChannels { + let _ = item.context.engine.peers.toggleRecommendedChannelsHidden(peerId: item.message.id.peerId, hidden: !recommendedChannels.isHidden).startStandalone() + } else { + let _ = item.context.engine.peers.requestRecommendedChannels(peerId: item.message.id.peerId).startStandalone() + } + } + + @objc private func closeButtonPressed() { + guard let item = self.item else { + return + } + let _ = item.context.engine.peers.toggleRecommendedChannelsHidden(peerId: item.message.id.peerId, hidden: true).startStandalone() + } + + override public func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize, _ avatarInset: CGFloat) -> (ChatMessageBubbleContentProperties, unboundSize: CGSize?, maxWidth: CGFloat, layout: (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) { + let makeLabelLayout = TextNode.asyncLayout(self.labelNode) + let makeTitleLayout = TextNode.asyncLayout(self.titleNode) + + let cachedMaskBackgroundImage = self.cachedMaskBackgroundImage + + return { item, layoutConstants, _, _, constrainedSize, _ in + let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: true, headerSpacing: 0.0, hidesBackground: .always, forceFullCorners: false, forceAlignment: .center) + + let unboundWidth: CGFloat = constrainedSize.width - 10.0 * 2.0 + return (contentProperties, nil, unboundWidth, { constrainedSize, position in + let attributedString = attributedServiceMessageString(theme: item.presentationData.theme, strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, message: EngineMessage(item.message), accountPeerId: item.context.account.peerId) + + let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: attributedString, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: constrainedSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets())) + + let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Chat_SimilarChannels, font: Font.semibold(15.0), textColor: item.presentationData.theme.theme.chat.message.incoming.primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: constrainedSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets())) + + var labelRects = labelLayout.linesRects() + if labelRects.count > 1 { + let sortedIndices = (0 ..< labelRects.count).sorted(by: { labelRects[$0].width > labelRects[$1].width }) + for i in 0 ..< sortedIndices.count { + let index = sortedIndices[i] + for j in -1 ... 1 { + if j != 0 && index + j >= 0 && index + j < sortedIndices.count { + if abs(labelRects[index + j].width - labelRects[index].width) < 40.0 { + labelRects[index + j].size.width = max(labelRects[index + j].width, labelRects[index].width) + labelRects[index].size.width = labelRects[index + j].size.width + } + } + } + } + } + for i in 0 ..< labelRects.count { + labelRects[i] = labelRects[i].insetBy(dx: -6.0, dy: floor((labelRects[i].height - 20.0) / 2.0)) + labelRects[i].size.height = 20.0 + labelRects[i].origin.x = floor((labelLayout.size.width - labelRects[i].width) / 2.0) + } + + let backgroundMaskImage: (CGPoint, UIImage)? + var backgroundMaskUpdated = false + if let (currentOffset, currentImage, currentRects) = cachedMaskBackgroundImage, currentRects == labelRects { + backgroundMaskImage = (currentOffset, currentImage) + } else { + backgroundMaskImage = LinkHighlightingNode.generateImage(color: .black, inset: 0.0, innerRadius: 10.0, outerRadius: 10.0, rects: labelRects, useModernPathCalculation: false) + backgroundMaskUpdated = true + } + + let isExpanded: Bool + if let recommendedChannels = item.associatedData.recommendedChannels, !recommendedChannels.channels.isEmpty && !recommendedChannels.isHidden { + isExpanded = true + } else { + isExpanded = false + } + + let spacing: CGFloat = 17.0 + let margin: CGFloat = 4.0 + var contentSize = CGSize(width: constrainedSize.width, height: labelLayout.size.height) + if isExpanded { + contentSize.height += spacing + 140.0 + margin + } else { + contentSize.height += margin + } + + return (contentSize.width, { boundingWidth in + return (contentSize, { [weak self] animation, synchronousLoads, info in + if let strongSelf = self { + let themeUpdated = strongSelf.item?.presentationData.theme !== item.presentationData.theme + strongSelf.item = item + strongSelf.isExpanded = isExpanded + + if !item.controllerInteraction.recommendedChannelsOpenUp { + info?.setInvertOffsetDirection() + } + + let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: labelLayout.size.height + spacing - 14.0), size: CGSize(width: constrainedSize.width, height: 140.0)) + + strongSelf.panelNode.position = CGPoint(x: panelFrame.midX, y: panelFrame.minY) + strongSelf.panelNode.bounds = CGRect(origin: .zero, size: panelFrame.size) + + let panelInnerSize = CGSize(width: panelFrame.width + 8.0, height: panelFrame.height + 10.0) + if let backgroundNode = item.controllerInteraction.presentationContext.backgroundNode { + let graphics = PresentationResourcesChat.principalGraphics(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) + strongSelf.panelBackgroundNode.update(size: panelInnerSize, theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, graphics: graphics, wallpaperBackgroundNode: backgroundNode, transition: .immediate) + } + strongSelf.panelBackgroundNode.frame = CGRect(origin: CGPoint(x: -7.0, y: -8.0), size: panelInnerSize) + + if strongSelf.panelBackgroundNode.layer.mask == nil { + strongSelf.panelBackgroundNode.layer.mask = strongSelf.panelMaskLayer + } + strongSelf.panelMaskLayer?.frame = CGRect(origin: .zero, size: panelInnerSize) + if strongSelf.panelMaskLayer?.path == nil { + let path = generateRoundedRectWithTailPath(rectSize: CGSize(width: panelFrame.width, height: panelFrame.height), cornerRadius: 16.0, tailSize: CGSize(width: 16.0, height: 6.0), tailRadius: 2.0, tailPosition: 0.5, transformTail: false) + path.apply(CGAffineTransform(translationX: 7.0, y: 2.0)) + strongSelf.panelMaskLayer?.path = path.cgPath + } + + if themeUpdated { + strongSelf.closeIconNode.image = generateCloseButtonImage(color: item.presentationData.theme.theme.chat.message.incoming.secondaryTextColor) + } + + let _ = labelApply() + let _ = titleApply() + + let labelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((contentSize.width - labelLayout.size.width) / 2.0), y: 2.0), size: labelLayout.size) + strongSelf.labelNode.frame = labelFrame + + let titleFrame = CGRect(origin: CGPoint(x: 16.0, y: 11.0), size: titleLayout.size) + strongSelf.titleNode.frame = titleFrame + + if let icon = strongSelf.closeIconNode.image { + let closeFrame = CGRect(origin: CGPoint(x: panelFrame.width - 5.0 - icon.size.width, y: 5.0), size: icon.size) + strongSelf.closeIconNode.frame = closeFrame + strongSelf.closeButtonNode.frame = closeFrame.insetBy(dx: -4.0, dy: -4.0) + } + + if isExpanded { + animation.animator.updateAlpha(layer: strongSelf.panelNode.layer, alpha: 1.0, completion: nil) + animation.animator.updateScale(layer: strongSelf.panelNode.layer, scale: 1.0, completion: nil) + } else { + animation.animator.updateAlpha(layer: strongSelf.panelNode.layer, alpha: 0.0, completion: nil) + animation.animator.updateScale(layer: strongSelf.panelNode.layer, scale: 0.1, completion: nil) + } + + let baseBackgroundFrame = labelFrame.offsetBy(dx: 0.0, dy: -11.0) + if let (offset, image) = backgroundMaskImage { + if strongSelf.backgroundNode == nil { + if let backgroundNode = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) { + strongSelf.backgroundNode = backgroundNode + strongSelf.insertSubnode(backgroundNode, at: 0) + + backgroundNode.view.addGestureRecognizer(UITapGestureRecognizer(target: strongSelf, action: #selector(strongSelf.pressed))) + } + } + + if backgroundMaskUpdated, let backgroundNode = strongSelf.backgroundNode { + if labelRects.count == 1 { + backgroundNode.clipsToBounds = true + backgroundNode.cornerRadius = labelRects[0].height / 2.0 + backgroundNode.view.mask = nil + } else { + backgroundNode.clipsToBounds = false + backgroundNode.cornerRadius = 0.0 + backgroundNode.view.mask = strongSelf.backgroundMaskNode.view + } + } + + if let backgroundNode = strongSelf.backgroundNode { + backgroundNode.frame = CGRect(origin: CGPoint(x: baseBackgroundFrame.minX + offset.x, y: baseBackgroundFrame.minY + offset.y), size: image.size) + } + strongSelf.backgroundMaskNode.image = image + strongSelf.backgroundMaskNode.frame = CGRect(origin: CGPoint(), size: image.size) + + strongSelf.cachedMaskBackgroundImage = (offset, image, labelRects) + } + if let (rect, size) = strongSelf.absoluteRect { + strongSelf.updateAbsoluteRect(rect, within: size) + } + + strongSelf.updateList() + } + }) + }) + }) + } + } + + private func updateList() { + guard let item = self.item, let recommendedChannels = item.associatedData.recommendedChannels else { + return + } + let listSize = self.panelListView.update( + transition: .immediate, + component: AnyComponent( + ChannelListPanelComponent( + context: item.context, + theme: item.presentationData.theme.theme, + strings: item.presentationData.strings, + peers: recommendedChannels, + action: { peer in + if let peer { + var jsonString: String = "{" + jsonString += "\"ref_channel_id\": \"\(item.message.id.peerId.id._internalGetInt64Value())\"," + jsonString += "\"open_channel_id\": \"\(peer.id.id._internalGetInt64Value())\"" + jsonString += "}" + + if let data = jsonString.data(using: .utf8), let json = JSON(data: data) { + addAppLogEvent(postbox: item.context.account.postbox, type: "channels.open_recommended_channel", data: json) + } + item.controllerInteraction.openPeer(peer, .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) + } else { + let context = item.context + let presentationData = item.context.sharedContext.currentPresentationData.with { $0 } + let controller = UndoOverlayController( + presentationData: presentationData, + content: .premiumPaywall(title: nil, text: item.presentationData.strings.Chat_ChannelRecommendation_PremiumTooltip, customUndoText: nil, timeout: nil, linkAction: nil), + elevatedLayout: false, + action: { [weak self] action in + if case .info = action { + if let self, let item = self.item { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .ads, forceDark: false, dismissed: nil) + item.controllerInteraction.navigationController()?.pushViewController(controller) + } + } + return true + } + ) + item.controllerInteraction.presentControllerInCurrent(controller, nil) + HapticFeedback().impact(.light) + } + }, + openMore: { [weak self] in + guard let item = self?.item else { + return + } + let _ = (item.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: item.message.id.peerId)) + |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in + if let peer = peer { + self?.item?.controllerInteraction.openPeer(peer, .info(ChatControllerInteractionNavigateToPeer.InfoParams(switchToRecommendedChannels: true)), nil, .default) + } + }) + }, + contextAction: { [weak self] peer, sourceView, gesture in + if let item = self?.item { + item.controllerInteraction.openRecommendedChannelContextMenu(peer, sourceView, gesture) + } + } + ) + ), + environment: {}, + containerSize: CGSize(width: self.panelNode.frame.width, height: 100.0) + ) + if let view = self.panelListView.view { + if view.superview == nil { + self.panelNode.view.addSubview(view) + } + view.frame = CGRect(origin: CGPoint(x: 0.0, y: 42.0), size: listSize) + } + } + + override public func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) { + self.absoluteRect = (rect, containerSize) + + if let backgroundNode = self.backgroundNode { + var backgroundFrame = backgroundNode.frame + backgroundFrame.origin.x += rect.minX + backgroundFrame.origin.y += rect.minY + backgroundNode.update(rect: backgroundFrame, within: containerSize, transition: .immediate) + } + + var panelBackgroundFrame = panelBackgroundNode.frame + panelBackgroundFrame.origin.x += self.panelNode.frame.minX + rect.minX + panelBackgroundFrame.origin.y += self.panelNode.frame.minY + rect.minY + self.panelBackgroundNode.updateAbsoluteRect(panelBackgroundFrame, within: containerSize) + } + + override public func applyAbsoluteOffset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) { + if let backgroundNode = self.backgroundNode { + backgroundNode.offset(value: value, animationCurve: animationCurve, duration: duration) + } + } + + override public func applyAbsoluteOffsetSpring(value: CGFloat, duration: Double, damping: CGFloat) { + if let backgroundNode = self.backgroundNode { + backgroundNode.offsetSpring(value: value, duration: duration, damping: damping) + } + } + + override public func updateTouchesAtPoint(_ point: CGPoint?) { + if let item = self.item { + var rects: [(CGRect, CGRect)]? + let textNodeFrame = self.labelNode.frame + if let point = point { + if let (index, attributes) = self.labelNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY - 10.0)) { + let possibleNames: [String] = [ + TelegramTextAttributes.URL, + TelegramTextAttributes.PeerMention, + TelegramTextAttributes.PeerTextMention, + TelegramTextAttributes.BotCommand, + TelegramTextAttributes.Hashtag + ] + for name in possibleNames { + if let _ = attributes[NSAttributedString.Key(rawValue: name)] { + rects = self.labelNode.lineAndAttributeRects(name: name, at: index) + break + } + } + } + } + + if let rects = rects { + var mappedRects: [CGRect] = [] + for i in 0 ..< rects.count { + let lineRect = rects[i].0 + var itemRect = rects[i].1 + itemRect.origin.x = floor((textNodeFrame.size.width - lineRect.width) / 2.0) + itemRect.origin.x + mappedRects.append(itemRect) + } + + let linkHighlightingNode: LinkHighlightingNode + if let current = self.linkHighlightingNode { + linkHighlightingNode = current + } else { + let serviceColor = serviceMessageColorComponents(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + linkHighlightingNode = LinkHighlightingNode(color: serviceColor.linkHighlight) + linkHighlightingNode.inset = 2.5 + self.linkHighlightingNode = linkHighlightingNode + self.insertSubnode(linkHighlightingNode, belowSubnode: self.labelNode) + } + linkHighlightingNode.frame = self.labelNode.frame.offsetBy(dx: 0.0, dy: 1.5) + linkHighlightingNode.updateRects(mappedRects) + } else if let linkHighlightingNode = self.linkHighlightingNode { + self.linkHighlightingNode = nil + linkHighlightingNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, removeOnCompletion: false, completion: { [weak linkHighlightingNode] _ in + linkHighlightingNode?.removeFromSupernode() + }) + } + } + } + + override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { + let textNodeFrame = self.labelNode.frame + if let (index, attributes) = self.labelNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY - 10.0)), gesture == .tap { + if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { + var concealed = true + if let (attributeText, fullText) = self.labelNode.attributeSubstring(name: TelegramTextAttributes.URL, index: index) { + concealed = !doesUrlMatchText(url: url, text: attributeText, fullText: fullText) + } + return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: url, concealed: concealed))) + } else if let peerMention = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerMention)] as? TelegramPeerMention { + return ChatMessageBubbleContentTapAction(content: .peerMention(peerId: peerMention.peerId, mention: peerMention.mention, openProfile: false)) + } else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String { + return ChatMessageBubbleContentTapAction(content: .textMention(peerName)) + } else if let botCommand = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.BotCommand)] as? String { + return ChatMessageBubbleContentTapAction(content: .botCommand(botCommand)) + } else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag { + return ChatMessageBubbleContentTapAction(content: .hashtag(hashtag.peerName, hashtag.hashtag)) + } + } + + if let backgroundNode = self.backgroundNode, backgroundNode.frame.contains(point) { + return ChatMessageBubbleContentTapAction(content: .ignore) + } + + if self.panelNode.frame.contains(point) { + let panelPoint = self.view.convert(point, to: self.panelNode.view) + if self.closeButtonNode.frame.contains(panelPoint) { + return ChatMessageBubbleContentTapAction(content: .ignore) + } + } + + return ChatMessageBubbleContentTapAction(content: .none) + } +} + +private class MessageBackgroundNode: ASDisplayNode { + private let backgroundWallpaperNode: ChatMessageBubbleBackdrop + private let backgroundNode: ChatMessageBackground + + override init() { + self.backgroundWallpaperNode = ChatMessageBubbleBackdrop() + self.backgroundNode = ChatMessageBackground() + self.backgroundNode.backdropNode = self.backgroundWallpaperNode + + super.init() + + self.addSubnode(self.backgroundNode) + self.addSubnode(self.backgroundWallpaperNode) + } + + private var absoluteRect: (CGRect, CGSize)? + + func update(size: CGSize, theme: PresentationTheme, wallpaper: TelegramWallpaper, graphics: PrincipalThemeEssentialGraphics, wallpaperBackgroundNode: WallpaperBackgroundNode, transition: ContainedViewLayoutTransition) { + self.backgroundNode.setType(type: .incoming(.Extracted), highlighted: false, graphics: graphics, maskMode: false, hasWallpaper: wallpaper.hasWallpaper, transition: transition, backgroundNode: wallpaperBackgroundNode) + self.backgroundWallpaperNode.setType(type: .incoming(.Extracted), theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), essentialGraphics: graphics, maskMode: false, backgroundNode: wallpaperBackgroundNode) + + let backgroundFrame = CGRect(origin: CGPoint(), size: size) + self.backgroundNode.updateLayout(size: backgroundFrame.size, transition: transition) + self.backgroundWallpaperNode.updateFrame(backgroundFrame, transition: transition) + + if let (rect, size) = self.absoluteRect { + self.updateAbsoluteRect(rect, within: size) + } + } + + func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) { + self.absoluteRect = (rect, containerSize) + + var backgroundWallpaperFrame = self.backgroundWallpaperNode.frame + backgroundWallpaperFrame.origin.x += rect.minX + backgroundWallpaperFrame.origin.y += rect.minY + self.backgroundWallpaperNode.update(rect: backgroundWallpaperFrame, within: containerSize) + } +} + +private let itemSize = CGSize(width: 84.0, height: 90.0) + +private final class ChannelItemComponent: Component { + let context: AccountContext + let theme: PresentationTheme + let strings: PresentationStrings + let peers: [EnginePeer] + let isLocked: Bool + let title: String + let subtitle: String + let action: (EnginePeer?) -> Void + let openMore: () -> Void + let contextAction: ((EnginePeer, UIView, ContextGesture?) -> Void)? + + init( + context: AccountContext, + theme: PresentationTheme, + strings: PresentationStrings, + peers: [EnginePeer], + isLocked: Bool, + title: String, + subtitle: String, + action: @escaping (EnginePeer?) -> Void, + openMore: @escaping () -> Void, + contextAction: ((EnginePeer, UIView, ContextGesture?) -> Void)? + ) { + self.context = context + self.theme = theme + self.strings = strings + self.peers = peers + self.isLocked = isLocked + self.title = title + self.subtitle = subtitle + self.action = action + self.openMore = openMore + self.contextAction = contextAction + } + + static func ==(lhs: ChannelItemComponent, rhs: ChannelItemComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.peers != rhs.peers { + return false + } + if lhs.isLocked != rhs.isLocked { + return false + } + if lhs.title != rhs.title { + return false + } + if lhs.subtitle != rhs.subtitle { + return false + } + return true + } + + final class View: UIView { + private let contextContainer: ContextControllerSourceView + private let containerButton: HighlightTrackingButton + + private let title = ComponentView() + private let subtitle = ComponentView() + + private let circlesView: UIImageView + + private let avatarNode: AvatarNode + private var mergedAvatarsNode: MergedAvatarsNode? + private let avatarBadge: AvatarBadgeView + private let subtitleIcon = ComponentView() + + private var component: ChannelItemComponent? + private weak var state: EmptyComponentState? + + override init(frame: CGRect) { + self.contextContainer = ContextControllerSourceView() + + self.circlesView = UIImageView() + + self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0)) + self.avatarNode.isUserInteractionEnabled = false + + self.avatarBadge = AvatarBadgeView(frame: CGRect()) + + self.containerButton = HighlightTrackingButton() + + super.init(frame: frame) + + self.addSubview(self.contextContainer) + + self.contextContainer.addSubview(self.containerButton) + + self.contextContainer.addSubnode(self.avatarNode) + self.contextContainer.addSubview(self.avatarBadge) + + self.avatarNode.badgeView = self.avatarBadge + + self.containerButton.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) + + self.contextContainer.animateScale = false + self.contextContainer.activated = { [weak self] gesture, point in + if let self, let component = self.component, let peer = component.peers.first { + component.contextAction?(peer, self.contextContainer, gesture) + } + } + + self.containerButton.highligthedChanged = { [weak self] highlighted in + if let self, self.bounds.width > 0.0 { + let topScale: CGFloat = (self.bounds.width - 6.0) / self.bounds.width + + if highlighted { + self.contextContainer.layer.removeAnimation(forKey: "sublayerTransform") + let transition = Transition(animation: .curve(duration: 0.2, curve: .easeInOut)) + transition.setScale(layer: self.contextContainer.layer, scale: topScale) + } else { + let transition = Transition(animation: .none) + transition.setScale(layer: self.contextContainer.layer, scale: 1.0) + + self.contextContainer.layer.animateScale(from: topScale, to: 1.0, duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false) + } + } + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func pressed() { + guard let component = self.component, let peer = component.peers.first else { + return + } + if !component.isLocked { + if component.peers.count > 1 { + component.openMore() + } else { + component.action(peer) + } + } else { + component.action(nil) + } + } + + func update(component: ChannelItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + let previousComponent = self.component + self.component = component + self.state = state + + let themeUpdated = previousComponent?.theme !== component.theme + + self.contextContainer.isGestureEnabled = true + + let titleSize = self.title.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: component.title, font: Font.regular(11.0), textColor: component.isLocked || component.peers.count > 1 ? component.theme.chat.message.incoming.secondaryTextColor : component.theme.chat.message.incoming.primaryTextColor)), + horizontalAlignment: .center, + maximumNumberOfLines: 2 + )), + environment: {}, + containerSize: CGSize(width: itemSize.width - 9.0, height: 100.0) + ) + + let subtitleSize = self.subtitle.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: component.subtitle, font: Font.with(size: 9.0, design: .round, weight: .bold), textColor: .white)) + )), + environment: {}, + containerSize: CGSize(width: itemSize.width - 6.0, height: 100.0) + ) + + var subtitleIconSize: CGSize + if component.peers.count == 1 { + subtitleIconSize = self.subtitleIcon.update( + transition: .immediate, + component: AnyComponent(BundleIconComponent(name: component.isLocked ? "Chat List/StatusLockIcon" : "Chat/Message/Subscriber", tintColor: .white)), + environment: {}, + containerSize: CGSize(width: itemSize.width - 6.0, height: 100.0) + ) + if component.isLocked { + subtitleIconSize = subtitleIconSize.fitted(CGSize(width: 8.0, height: 8.0)) + } + } else { + subtitleIconSize = .zero + } + + let avatarSize = CGSize(width: 60.0, height: 60.0) + var avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((itemSize.width - avatarSize.width) / 2.0), y: 0.0), size: avatarSize) + let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((itemSize.width - titleSize.width) / 2.0), y: avatarFrame.maxY + 4.0), size: titleSize) + + let subtitleSpacing: CGFloat = 1.0 + UIScreenPixel + let subtitleTotalWidth = subtitleIconSize.width + subtitleSize.width + subtitleSpacing + + let subtitleOriginX = floorToScreenPixels((itemSize.width - subtitleTotalWidth) / 2.0) + 1.0 - UIScreenPixel + var iconOriginX = subtitleOriginX + var textOriginX = subtitleOriginX + if subtitleIconSize.width > 0.0 { + textOriginX += subtitleIconSize.width + subtitleSpacing + } + if component.isLocked { + textOriginX = subtitleOriginX + iconOriginX = subtitleOriginX + subtitleSize.width + subtitleSpacing + } + + let subtitleIconFrame = CGRect(origin: CGPoint(x: iconOriginX, y: avatarFrame.maxY - subtitleSize.height + 1.0 - UIScreenPixel), size: subtitleIconSize) + let subtitleFrame = CGRect(origin: CGPoint(x: textOriginX, y: avatarFrame.maxY - subtitleSize.height - UIScreenPixel), size: subtitleSize) + + var avatarHorizontalOffset: CGFloat = 0.0 + if component.isLocked { + avatarHorizontalOffset = -10.0 + } + avatarFrame = avatarFrame.offsetBy(dx: avatarHorizontalOffset, dy: 0.0) + self.avatarNode.frame = avatarFrame + if let peer = component.peers.first { + self.avatarNode.setPeer(context: component.context, theme: component.theme, peer: peer) + } + + if component.peers.count > 1 { + let mergedAvatarsNode: MergedAvatarsNode + if let current = self.mergedAvatarsNode { + mergedAvatarsNode = current + } else { + mergedAvatarsNode = MergedAvatarsNode() + mergedAvatarsNode.isUserInteractionEnabled = false + self.contextContainer.insertSubview(mergedAvatarsNode.view, aboveSubview: self.avatarNode.view) + self.mergedAvatarsNode = mergedAvatarsNode + } + + mergedAvatarsNode.update(context: component.context, peers: component.peers.map { $0._asPeer() }, synchronousLoad: false, imageSize: 60.0, imageSpacing: 10.0, borderWidth: 2.0) + let avatarsSize = CGSize(width: avatarSize.width + 20.0, height: avatarSize.height) + mergedAvatarsNode.updateLayout(size: avatarsSize) + mergedAvatarsNode.frame = CGRect(origin: CGPoint(x: avatarFrame.midX - avatarsSize.width / 2.0, y: avatarFrame.minY), size: avatarsSize) + self.avatarNode.isHidden = true + } else { + self.mergedAvatarsNode?.view.removeFromSuperview() + self.mergedAvatarsNode = nil + self.avatarNode.isHidden = false + } + + if component.isLocked { + if self.circlesView.superview == nil { + self.contextContainer.insertSubview(self.circlesView, belowSubview: self.avatarNode.view) + } + self.circlesView.isHidden = false + + if self.circlesView.image == nil || themeUpdated { + let image = generateImage(CGSize(width: 50.0, height: avatarSize.height), rotatedContext: { size, context in + context.clear(CGRect(origin: .zero, size: size)) + + let color = component.theme.chat.message.incoming.secondaryTextColor.withMultipliedAlpha(0.35) + + context.saveGState() + + let rect1 = CGRect(origin: CGPoint(x: size.width - avatarSize.width, y: 0.0), size: avatarSize) + context.addEllipse(in: rect1) + context.clip() + + context.setFillColor(color.cgColor) + context.fill(rect1) + + context.restoreGState() + + context.setBlendMode(.clear) + context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - avatarSize.width - 12.0, y: -2.0), size: CGSize(width: avatarSize.width + 4.0, height: avatarSize.height + 4.0))) + + context.setBlendMode(.normal) + + context.saveGState() + + let rect2 = CGRect(origin: CGPoint(x: size.width - avatarSize.width - 10.0, y: 0.0), size: avatarSize) + context.addEllipse(in: rect2) + context.clip() + + context.setFillColor(color.cgColor) + context.fill(rect2) + + context.restoreGState() + + context.setBlendMode(.clear) + context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - avatarSize.width - 22.0, y: -2.0), size: CGSize(width: avatarSize.width + 4.0, height: avatarSize.height + 4.0))) + }) + self.circlesView.image = image + } + self.circlesView.frame = CGRect(origin: CGPoint(x: avatarFrame.midX, y: 0.0), size: CGSize(width: 50.0, height: 60.0)) + } else { + if self.circlesView.superview != nil { + self.circlesView.removeFromSuperview() + } + } + + if let titleView = self.title.view { + if titleView.superview == nil { + titleView.isUserInteractionEnabled = false + self.contextContainer.addSubview(titleView) + } + titleView.frame = titleFrame + } + if let subtitleView = self.subtitle.view { + if subtitleView.superview == nil { + subtitleView.isUserInteractionEnabled = false + self.contextContainer.addSubview(subtitleView) + } + subtitleView.frame = subtitleFrame + } + if let subtitleIconView = self.subtitleIcon.view { + if subtitleIconView.superview == nil { + subtitleIconView.isUserInteractionEnabled = false + self.contextContainer.addSubview(subtitleIconView) + } + subtitleIconView.frame = subtitleIconFrame + } + + let strokeWidth: CGFloat = 1.0 + UIScreenPixel + let avatarBadgeSize = CGSize(width: subtitleSize.width + subtitleIconSize.width + 10.0, height: 15.0) + self.avatarBadge.update(size: avatarBadgeSize, text: "", hasTimeoutIcon: false, useSolidColor: true, strokeColor: component.theme.chat.message.incoming.bubble.withoutWallpaper.fill.first!) + + let avatarBadgeFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((itemSize.width - avatarBadgeSize.width) / 2.0), y: avatarFrame.minY + avatarFrame.height - avatarBadgeSize.height + 2.0), size: avatarBadgeSize).insetBy(dx: -strokeWidth, dy: -strokeWidth) + self.avatarBadge.frame = avatarBadgeFrame + + let bounds = CGRect(origin: .zero, size: itemSize) + self.contextContainer.frame = bounds + self.containerButton.frame = bounds + + return itemSize + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +private let channelsLimit: Int32 = 10 + +final class ChannelListPanelComponent: Component { + typealias EnvironmentType = Empty + + let context: AccountContext + let theme: PresentationTheme + let strings: PresentationStrings + let peers: RecommendedChannels + let action: (EnginePeer?) -> Void + let openMore: () -> Void + let contextAction: (EnginePeer, UIView, ContextGesture?) -> Void + + init( + context: AccountContext, + theme: PresentationTheme, + strings: PresentationStrings, + peers: RecommendedChannels, + action: @escaping (EnginePeer?) -> Void, + openMore: @escaping () -> Void, + contextAction: @escaping (EnginePeer, UIView, ContextGesture?) -> Void + ) { + self.context = context + self.theme = theme + self.strings = strings + self.peers = peers + self.action = action + self.openMore = openMore + self.contextAction = contextAction + } + + static func ==(lhs: ChannelListPanelComponent, rhs: ChannelListPanelComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.peers != rhs.peers { + return false + } + return true + } + + private struct ItemLayout: Equatable { + let containerInsets: UIEdgeInsets + let containerHeight: CGFloat + let itemWidth: CGFloat + let itemCount: Int + + let contentWidth: CGFloat + + init( + containerInsets: UIEdgeInsets, + containerHeight: CGFloat, + itemWidth: CGFloat, + itemCount: Int + ) { + self.containerInsets = containerInsets + self.containerHeight = containerHeight + self.itemWidth = itemWidth + self.itemCount = itemCount + + self.contentWidth = containerInsets.left + containerInsets.right + CGFloat(itemCount) * itemWidth + } + + func visibleItems(for rect: CGRect) -> Range? { + let offsetRect = rect.offsetBy(dx: -self.containerInsets.left, dy: -self.containerInsets.top) + var minVisibleRow = Int(floor((offsetRect.minX) / (self.itemWidth))) + minVisibleRow = max(0, minVisibleRow) + let maxVisibleRow = Int(ceil((offsetRect.maxX) / (self.itemWidth))) + + let minVisibleIndex = minVisibleRow + let maxVisibleIndex = maxVisibleRow + + if maxVisibleIndex >= minVisibleIndex { + return minVisibleIndex ..< (maxVisibleIndex + 1) + } else { + return nil + } + } + + func itemFrame(for index: Int) -> CGRect { + return CGRect(origin: CGPoint(x: self.containerInsets.left + CGFloat(index) * self.itemWidth, y: 0.0), size: CGSize(width: self.itemWidth, height: self.containerHeight)) + } + } + + private final class ScrollViewImpl: UIScrollView { + override func touchesShouldCancel(in view: UIView) -> Bool { + return true + } + } + + class View: UIView, UIScrollViewDelegate { + private let scrollView: ScrollViewImpl + + private let measureItem = ComponentView() + private var visibleItems: [EnginePeer.Id: ComponentView] = [:] + + private var ignoreScrolling: Bool = false + + private var component: ChannelListPanelComponent? + private var itemLayout: ItemLayout? + + override init(frame: CGRect) { + self.scrollView = ScrollViewImpl() + + super.init(frame: frame) + + self.scrollView.delaysContentTouches = true + self.scrollView.canCancelContentTouches = true + self.scrollView.clipsToBounds = false + if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { + self.scrollView.contentInsetAdjustmentBehavior = .never + } + if #available(iOS 13.0, *) { + self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false + } + self.scrollView.showsVerticalScrollIndicator = false + self.scrollView.showsHorizontalScrollIndicator = false + self.scrollView.alwaysBounceHorizontal = true + self.scrollView.scrollsToTop = false + self.scrollView.delegate = self + self.scrollView.clipsToBounds = true + self.addSubview(self.scrollView) + + self.disablesInteractiveTransitionGestureRecognizer = true + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + if !self.ignoreScrolling { + self.updateScrolling(transition: .immediate) + } + } + + private func updateScrolling(transition: Transition) { + guard let component = self.component, let itemLayout = self.itemLayout else { + return + } + + let visibleBounds = self.scrollView.bounds.insetBy(dx: -100.0, dy: 0.0) + + let hasMore = component.peers.count > channelsLimit + + var validIds = Set() + if let visibleItems = itemLayout.visibleItems(for: visibleBounds) { + let upperBound = min(Int(channelsLimit), visibleItems.upperBound) + + for index in visibleItems.lowerBound ..< upperBound { + if index >= component.peers.channels.count { + continue + } + let item = component.peers.channels[index] + let id = item.peer.id + validIds.insert(id) + + var itemTransition = transition + let itemView: ComponentView + if let current = self.visibleItems[id] { + itemView = current + } else { + itemTransition = .immediate + itemView = ComponentView() + self.visibleItems[id] = itemView + } + + let title: String + let subtitle: String + var isLocked = false + var isLast = false + if index == upperBound - 1 && hasMore { + if !component.context.isPremium { + isLocked = true + } + title = component.strings.Chat_SimilarChannels_MoreChannels + subtitle = "+\(component.peers.count - channelsLimit)" + isLast = true + } else { + title = item.peer.compactDisplayTitle + subtitle = countString(Int64(item.subscribers)) + } + + var peers: [EnginePeer] = [item.peer] + if isLast && !isLocked { + for i in index + 1 ..< index + 3 { + if i < component.peers.channels.count { + peers.append(component.peers.channels[i].peer) + } + } + } + + let _ = itemView.update( + transition: itemTransition, + component: AnyComponent(ChannelItemComponent( + context: component.context, + theme: component.theme, + strings: component.strings, + peers: peers, + isLocked: isLocked, + title: title, + subtitle: subtitle, + action: component.action, + openMore: component.openMore, + contextAction: !isLocked && peers.count == 1 ? component.contextAction : nil + )), + environment: {}, + containerSize: CGSize(width: itemLayout.itemWidth, height: itemLayout.containerHeight) + ) + let itemFrame = itemLayout.itemFrame(for: index) + if let itemComponentView = itemView.view { + if itemComponentView.superview == nil { + self.scrollView.addSubview(itemComponentView) + } + itemTransition.setFrame(view: itemComponentView, frame: itemFrame) + } + } + } + + var removeIds: [EnginePeer.Id] = [] + for (id, itemView) in self.visibleItems { + if !validIds.contains(id) { + removeIds.append(id) + if let itemComponentView = itemView.view { + transition.setAlpha(view: itemComponentView, alpha: 0.0, completion: { [weak itemComponentView] _ in + itemComponentView?.removeFromSuperview() + }) + } + } + } + for id in removeIds { + self.visibleItems.removeValue(forKey: id) + } + } + + func update(component: ChannelListPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + self.component = component + + let itemLayout = ItemLayout( + containerInsets: UIEdgeInsets(top: 0.0, left: 4.0, bottom: 0.0, right: 4.0), + containerHeight: availableSize.height, + itemWidth: itemSize.width, + itemCount: min(Int(channelsLimit), component.peers.channels.count) + ) + self.itemLayout = itemLayout + + self.ignoreScrolling = true + let contentOffset = self.scrollView.bounds.minY + transition.setPosition(view: self.scrollView, position: CGRect(origin: CGPoint(), size: availableSize).center) + var scrollBounds = self.scrollView.bounds + scrollBounds.size = availableSize + transition.setBounds(view: self.scrollView, bounds: scrollBounds) + let contentSize = CGSize(width: itemLayout.contentWidth, height: availableSize.height) + if self.scrollView.contentSize != contentSize { + self.scrollView.contentSize = contentSize + } + if !transition.animation.isImmediate && self.scrollView.bounds.minY != contentOffset { + let deltaOffset = self.scrollView.bounds.minY - contentOffset + transition.animateBoundsOrigin(view: self.scrollView, from: CGPoint(x: 0.0, y: -deltaOffset), to: CGPoint(), additive: true) + } + self.ignoreScrolling = false + self.updateScrolling(transition: transition) + + return availableSize + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/BUILD new file mode 100644 index 00000000000..e65d172e967 --- /dev/null +++ b/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/BUILD @@ -0,0 +1,26 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ChatMessageSelectionInputPanelNode", + module_name = "ChatMessageSelectionInputPanelNode", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/Postbox", + "//submodules/TelegramCore", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/TelegramPresentationData", + "//submodules/AppBundle", + "//submodules/ChatPresentationInterfaceState", + "//submodules/TelegramUI/Components/Chat/ChatInputPanelNode", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Sources/ChatMessageSelectionInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/Sources/ChatMessageSelectionInputPanelNode.swift similarity index 95% rename from submodules/TelegramUI/Sources/ChatMessageSelectionInputPanelNode.swift rename to submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/Sources/ChatMessageSelectionInputPanelNode.swift index 6b406dc5487..9c6f5d7ad95 100644 --- a/submodules/TelegramUI/Sources/ChatMessageSelectionInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/Sources/ChatMessageSelectionInputPanelNode.swift @@ -11,7 +11,7 @@ import AppBundle import ChatPresentationInterfaceState import ChatInputPanelNode -final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { +public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { private let deleteButton: HighlightableButtonNode private let reportButton: HighlightableButtonNode private let forwardButton: HighlightableButtonNode @@ -32,7 +32,7 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { private let canDeleteMessagesDisposable = MetaDisposable() - var selectedMessages = Set() { + public var selectedMessages = Set() { didSet { if oldValue != self.selectedMessages { self.forwardButton.isEnabled = self.selectedMessages.count != 0 @@ -63,7 +63,7 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { } } - init(theme: PresentationTheme, strings: PresentationStrings, peerMedia: Bool = false) { + public init(theme: PresentationTheme, strings: PresentationStrings, peerMedia: Bool = false) { self.theme = theme self.peerMedia = peerMedia @@ -152,7 +152,7 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { self.canDeleteMessagesDisposable.dispose() } - func updateTheme(theme: PresentationTheme) { + public func updateTheme(theme: PresentationTheme) { if self.theme !== theme { self.theme = theme @@ -177,15 +177,15 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { } } - @objc func deleteButtonPressed() { + @objc private func deleteButtonPressed() { self.interfaceInteraction?.deleteSelectedMessages() } - @objc func reportButtonPressed() { + @objc private func reportButtonPressed() { self.interfaceInteraction?.reportSelectedMessages() } - @objc func forwardButtonPressed() { + @objc private func forwardButtonPressed() { if let _ = self.presentationInterfaceState?.renderedPeer?.peer as? TelegramSecretChat { return } @@ -231,7 +231,7 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { } // - @objc func shareButtonPressed() { + @objc private func shareButtonPressed() { if let _ = self.presentationInterfaceState?.renderedPeer?.peer as? TelegramSecretChat { return } @@ -242,7 +242,7 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { } } - override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat { + override public func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat { self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary, isMediaInputExpanded) let panelHeight = defaultHeight(metrics: metrics) @@ -366,7 +366,7 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { return panelHeight } - override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { + override public func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { return defaultHeight(metrics: metrics) } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift index 733f0a92341..1772b20bb70 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift @@ -1371,7 +1371,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { for attribute in item.message.attributes { if let attribute = attribute as? ReplyMessageAttribute { return .optionalAction({ - item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote?.text : nil)) + item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote.flatMap { quote in NavigateToMessageParams.Quote(string: quote.text, offset: quote.offset) } : nil)) }) } else if let attribute = attribute as? ReplyStoryAttribute { return .optionalAction({ @@ -1400,7 +1400,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { } item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId, NavigateToMessageParams(timestamp: nil, quote: nil)) } else if let peer = forwardInfo.source ?? forwardInfo.author { - item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) + item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info(nil) : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) } else if let _ = forwardInfo.authorSignature { item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil) } @@ -2052,4 +2052,21 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { override public func contentFrame() -> CGRect { return self.imageNode.frame } + + override public func makeContentSnapshot() -> (UIImage, CGRect)? { + UIGraphicsBeginImageContextWithOptions(self.imageNode.view.bounds.size, false, 0.0) + let context = UIGraphicsGetCurrentContext()! + + context.translateBy(x: -self.imageNode.frame.minX, y: -self.imageNode.frame.minY) + self.view.layer.render(in: context) + + let image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + guard let image else { + return nil + } + + return (image, self.imageNode.frame) + } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift index 100fff36e7b..3e9e8bfe852 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift @@ -53,6 +53,34 @@ private final class CachedChatMessageText { } } +private func findQuoteRange(string: String, quoteText: String, offset: Int?) -> NSRange? { + let nsString = string as NSString + var currentRange: NSRange? + while true { + let startOffset = currentRange?.upperBound ?? 0 + let range = nsString.range(of: quoteText, range: NSRange(location: startOffset, length: nsString.length - startOffset)) + if range.location != NSNotFound { + if let offset { + if let currentRangeValue = currentRange { + if abs(range.location - offset) > abs(currentRangeValue.location - offset) { + break + } else { + currentRange = range + } + } else { + currentRange = range + } + } else { + currentRange = range + break + } + } else { + break + } + } + return currentRange +} + public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { private let containerNode: ASDisplayNode private let textNode: TextNodeWithEntities @@ -407,7 +435,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { if item.presentationData.theme.theme.overallDarkAppearance { codeBlockTitleColor = .white codeBlockAccentColor = UIColor(white: 1.0, alpha: 0.5) - codeBlockBackgroundColor = UIColor(white: 0.0, alpha: 0.65) + codeBlockBackgroundColor = UIColor(white: 0.0, alpha: 0.25) } else { codeBlockTitleColor = mainColor codeBlockAccentColor = mainColor @@ -1109,15 +1137,13 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { return nil } - public func getQuoteRect(quote: String) -> CGRect? { + public func getQuoteRect(quote: String, offset: Int?) -> CGRect? { var rectsSet: [CGRect] = [] if !quote.isEmpty, let cachedLayout = self.textNode.textNode.cachedLayout, let string = cachedLayout.attributedString?.string { - let nsString = string as NSString - let range = nsString.range(of: quote) - if range.location != NSNotFound { - if let rects = cachedLayout.rangeRects(in: range)?.rects, !rects.isEmpty { - rectsSet = rects - } + + let range = findQuoteRange(string: string, quoteText: quote, offset: offset) + if let range, let rects = cachedLayout.rangeRects(in: range)?.rects, !rects.isEmpty { + rectsSet = rects } } if !rectsSet.isEmpty { @@ -1136,15 +1162,13 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { return nil } - public func updateQuoteTextHighlightState(text: String?, color: UIColor, animated: Bool) { + public func updateQuoteTextHighlightState(text: String?, offset: Int?, color: UIColor, animated: Bool) { var rectsSet: [CGRect] = [] if let text = text, !text.isEmpty, let cachedLayout = self.textNode.textNode.cachedLayout, let string = cachedLayout.attributedString?.string { - let nsString = string as NSString - let range = nsString.range(of: text) - if range.location != NSNotFound { - if let rects = cachedLayout.rangeRects(in: range)?.rects, !rects.isEmpty { - rectsSet = rects - } + + let quoteRange = findQuoteRange(string: string, quoteText: text, offset: offset) + if let quoteRange, let rects = cachedLayout.rangeRects(in: quoteRange)?.rects, !rects.isEmpty { + rectsSet = rects } } if !rectsSet.isEmpty { @@ -1339,12 +1363,12 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { private func getSelectionState(range: NSRange?) -> ChatControllerSubject.MessageOptionsInfo.SelectionState { var quote: ChatControllerSubject.MessageOptionsInfo.Quote? if let item = self.item, let range, let selection = self.getCurrentTextSelection(customRange: range) { - quote = ChatControllerSubject.MessageOptionsInfo.Quote(messageId: item.message.id, text: selection.text) + quote = ChatControllerSubject.MessageOptionsInfo.Quote(messageId: item.message.id, text: selection.text, offset: selection.offset) } return ChatControllerSubject.MessageOptionsInfo.SelectionState(canQuote: true, quote: quote) } - public func getCurrentTextSelection(customRange: NSRange? = nil) -> (text: String, entities: [MessageTextEntity])? { + public func getCurrentTextSelection(customRange: NSRange? = nil) -> (text: String, entities: [MessageTextEntity], offset: Int)? { guard let textSelectionNode = self.textSelectionNode else { return nil } @@ -1359,13 +1383,14 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } let nsString = string.string as NSString let substring = nsString.substring(with: range) + let offset = range.location var entities: [MessageTextEntity] = [] if let textEntitiesAttribute = item.message.textEntitiesAttribute { entities = messageTextEntitiesInRange(entities: textEntitiesAttribute.entities, range: range, onlyQuoteable: true) } - return (substring, entities) + return (substring, entities, offset) } public func animateClippingTransition(offset: CGFloat, animation: ListViewItemUpdateAnimation) { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode/Sources/ChatMessageWallpaperBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode/Sources/ChatMessageWallpaperBubbleContentNode.swift index 4d23692b072..792c9bbb94d 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode/Sources/ChatMessageWallpaperBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode/Sources/ChatMessageWallpaperBubbleContentNode.swift @@ -182,7 +182,21 @@ public class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode guard let item = self.item else { return } - let _ = item.controllerInteraction.openMessage(item.message, OpenMessageParams(mode: .default)) + + var canRemove = false + if item.message.effectivelyIncoming(item.context.account.peerId) { + if let media = item.message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case let .setChatWallpaper(wallpaper, forBoth) = media.action { + if forBoth, item.presentationData.theme.wallpaper.isBasicallyEqual(to: wallpaper) { + canRemove = true + } + } + } + + if canRemove { + let _ = item.context.engine.themes.revertChatWallpaper(peerId: item.message.id.peerId).startStandalone() + } else { + let _ = item.controllerInteraction.openMessage(item.message, OpenMessageParams(mode: .default)) + } } private func updateProgress(_ progress: Float?) { @@ -225,12 +239,14 @@ public class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode let primaryTextColor = serviceMessageColorComponents(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper).primaryText var wallpaper: TelegramWallpaper? - if let media = item.message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case let .setChatWallpaper(wallpaperValue) = media.action { + var forBoth = false + if let media = item.message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case let .setChatWallpaper(wallpaperValue, forBothValue) = media.action { wallpaper = wallpaperValue + forBoth = forBothValue } var mediaUpdated = true - if let wallpaper = wallpaper, let media = currentItem?.message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case let .setChatWallpaper(currentWallpaper) = media.action { + if let wallpaper = wallpaper, let media = currentItem?.message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case let .setChatWallpaper(currentWallpaper, _) = media.action { mediaUpdated = wallpaper != currentWallpaper } @@ -249,7 +265,11 @@ public class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode text = item.presentationData.strings.Notification_YouChangingWallpaper displayTrailingAnimatedDots = true } else { - text = item.presentationData.strings.Notification_YouChangedWallpaper + if forBoth { + text = item.presentationData.strings.Notification_YouChangedWallpaperBoth(peerName).string + } else { + text = item.presentationData.strings.Notification_YouChangedWallpaper + } } } else { text = item.presentationData.strings.Notification_ChangedWallpaper(peerName).string @@ -269,7 +289,14 @@ public class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: subtitle, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets())) - let (buttonTitleLayout, buttonTitleApply) = makeButtonTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Notification_Wallpaper_View, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets())) + let buttonText: String + if let wallpaper, forBoth && item.presentationData.theme.wallpaper.isBasicallyEqual(to: wallpaper) { + buttonText = "Remove" + } else { + buttonText = item.presentationData.strings.Notification_Wallpaper_View + } + + let (buttonTitleLayout, buttonTitleApply) = makeButtonTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: buttonText, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets())) var textHeight = subtitleLayout.size.height if displayTrailingAnimatedDots { @@ -463,6 +490,8 @@ public class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.statusOverlayNode.alpha > 0.0 { return ChatMessageBubbleContentTapAction(content: .none) + } else if self.buttonNode.frame.contains(point) { + return ChatMessageBubbleContentTapAction(content: .ignore) } else if self.mediaBackgroundNode.frame.contains(point) { return ChatMessageBubbleContentTapAction(content: .openMessage) } else { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift index 6634fce5e59..0ef76df20d6 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift @@ -479,8 +479,15 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent } } } else if let adAttribute = item.message.adAttribute { - title = nil - subtitle = nil + switch adAttribute.messageType { + case .sponsored: + title = item.presentationData.strings.Message_AdSponsoredLabel + case .recommended: + title = item.presentationData.strings.Message_AdRecommendedLabel + } + subtitle = item.message.author.flatMap { + NSAttributedString(string: EnginePeer($0).compactDisplayTitle, font: titleFont) + } text = item.message.text for attribute in item.message.attributes { if let attribute = attribute as? TextEntitiesMessageAttribute { @@ -496,8 +503,14 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent } } - if let author = item.message.author as? TelegramUser, author.botInfo != nil { - actionTitle = item.presentationData.strings.Conversation_ViewBot + if let buttonText = adAttribute.buttonText { + actionTitle = buttonText.uppercased() + } else if let author = item.message.author as? TelegramUser, author.botInfo != nil { + if case .botApp = adAttribute.target { + actionTitle = item.presentationData.strings.Conversation_LaunchApp + } else { + actionTitle = item.presentationData.strings.Conversation_ViewBot + } } else if let author = item.message.author as? TelegramChannel, case .group = author.info { if case let .peer(_, messageId, _) = adAttribute.target, messageId != nil { actionTitle = item.presentationData.strings.Conversation_ViewPost @@ -514,7 +527,7 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent actionTitle = item.presentationData.strings.Conversation_ViewChannel } } - displayLine = false + displayLine = true } let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, item.read, item.chatLocation, title, subtitle, text, entities, mediaAndFlags, badge, actionIcon, actionTitle, displayLine, layoutConstants, preparePosition, constrainedSize, item.controllerInteraction.presentationContext.animationCache, item.controllerInteraction.presentationContext.animationRenderer) diff --git a/submodules/TelegramUI/Components/Chat/ChatNavigationButton/Sources/ChatNavigationButton.swift b/submodules/TelegramUI/Components/Chat/ChatNavigationButton/Sources/ChatNavigationButton.swift index 3feb62f0d88..d273af5b4ed 100644 --- a/submodules/TelegramUI/Components/Chat/ChatNavigationButton/Sources/ChatNavigationButton.swift +++ b/submodules/TelegramUI/Components/Chat/ChatNavigationButton/Sources/ChatNavigationButton.swift @@ -2,7 +2,7 @@ import Foundation import UIKit public enum ChatNavigationButtonAction: Equatable { - case openChatInfo(expandAvatar: Bool) + case openChatInfo(expandAvatar: Bool, recommendedChannels: Bool) case clearHistory case clearCache case cancelMessageSelection diff --git a/submodules/TelegramUI/Components/Chat/ChatQrCodeScreen/BUILD b/submodules/TelegramUI/Components/Chat/ChatQrCodeScreen/BUILD new file mode 100644 index 00000000000..8ee4eade32e --- /dev/null +++ b/submodules/TelegramUI/Components/Chat/ChatQrCodeScreen/BUILD @@ -0,0 +1,51 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ChatQrCodeScreen", + module_name = "ChatQrCodeScreen", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/AsyncDisplayKit", + "//submodules/Postbox", + "//submodules/TelegramCore", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/AccountContext", + "//submodules/SolidRoundedButtonNode", + "//submodules/TelegramPresentationData", + "//submodules/TelegramUIPreferences", + "//submodules/TelegramNotices", + "//submodules/PresentationDataUtils", + "//submodules/AnimationUI", + "//submodules/MergeLists", + "//submodules/MediaResources", + "//submodules/StickerResources", + "//submodules/WallpaperResources", + "//submodules/TooltipUI", + "//submodules/AnimatedStickerNode", + "//submodules/TelegramAnimatedStickerNode", + "//submodules/ShimmerEffect", + "//submodules/WallpaperBackgroundNode", + "//submodules/QrCode", + "//submodules/AvatarNode", + "//submodules/ShareController", + "//submodules/TelegramStringFormatting", + "//submodules/PhotoResources", + "//submodules/TextFormat", + "//submodules/MediaPlayer:UniversalMediaPlayer", + "//submodules/TelegramUniversalVideoContent", + "//submodules/GalleryUI", + "//submodules/SaveToCameraRoll", + "//submodules/SegmentedControlNode", + "//submodules/AnimatedCountLabelNode", + "//submodules/HexColor", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift b/submodules/TelegramUI/Components/Chat/ChatQrCodeScreen/Sources/ChatQrCodeScreen.swift similarity index 99% rename from submodules/TelegramUI/Sources/ChatQrCodeScreen.swift rename to submodules/TelegramUI/Components/Chat/ChatQrCodeScreen/Sources/ChatQrCodeScreen.swift index 4e342ba6991..deba00c4933 100644 --- a/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift +++ b/submodules/TelegramUI/Components/Chat/ChatQrCodeScreen/Sources/ChatQrCodeScreen.swift @@ -34,6 +34,7 @@ import GalleryUI import SaveToCameraRoll import SegmentedControlNode import AnimatedCountLabelNode +import HexColor private func closeButtonImage(theme: PresentationTheme) -> UIImage? { return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in @@ -560,15 +561,15 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { } } -final class ChatQrCodeScreen: ViewController { - static let themeCrossfadeDuration: Double = 0.3 - static let themeCrossfadeDelay: Double = 0.05 +public final class ChatQrCodeScreen: ViewController { + public static let themeCrossfadeDuration: Double = 0.3 + public static let themeCrossfadeDelay: Double = 0.05 - enum Subject { + public enum Subject { case peer(peer: Peer, threadId: Int64?, temporary: Bool) case messages([Message]) - var fileName: String { + public var fileName: String { switch self { case let .peer(peer, threadId, _): var result: String @@ -608,9 +609,9 @@ final class ChatQrCodeScreen: ViewController { private var presentationThemePromise = Promise() private var presentationDataDisposable: Disposable? - var dismissed: (() -> Void)? + public var dismissed: (() -> Void)? - init(context: AccountContext, subject: ChatQrCodeScreen.Subject) { + public init(context: AccountContext, subject: ChatQrCodeScreen.Subject) { self.context = context self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.subject = subject @@ -641,7 +642,7 @@ final class ChatQrCodeScreen: ViewController { self.ready.set(self.controllerNode.ready.get()) } - required init(coder aDecoder: NSCoder) { + required public init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -1265,8 +1266,11 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg let newIconColors = iconColors(theme: self.presentationData.theme) if !self.switchThemeButton.isUserInteractionEnabled { - Queue.mainQueue().after(ChatThemeScreen.themeCrossfadeDelay) { - self.switchThemeIconAnimator = DisplayLinkAnimator(duration: ChatThemeScreen.themeCrossfadeDuration * UIView.animationDurationFactor(), from: 0.0, to: 1.0, update: { [weak self] value in + let themeCrossfadeDuration: Double = 0.3 + let themeCrossfadeDelay: Double = 0.25 + + Queue.mainQueue().after(themeCrossfadeDelay) { + self.switchThemeIconAnimator = DisplayLinkAnimator(duration: themeCrossfadeDuration * UIView.animationDurationFactor(), from: 0.0, to: 1.0, update: { [weak self] value in self?.animationNode.setColors(colors: interpolateColors(from: previousIconColors, to: newIconColors, fraction: value)) }, completion: { [weak self] in self?.switchThemeIconAnimator?.invalidate() @@ -1278,7 +1282,7 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg } } - override func didLoad() { + override public func didLoad() { super.didLoad() self.wrappingScrollNode.view.delegate = self @@ -1289,11 +1293,11 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg self.listNode.view.disablesInteractiveTransitionGestureRecognizer = true } - @objc func cancelButtonPressed() { + @objc private func cancelButtonPressed() { self.cancel?() } - @objc func switchThemePressed() { + @objc private func switchThemePressed() { self.switchThemeButton.isUserInteractionEnabled = false Queue.mainQueue().after(0.5) { self.switchThemeButton.isUserInteractionEnabled = true @@ -1376,7 +1380,7 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg } private var animatedOut = false - func animateIn() { + public func animateIn() { let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring) @@ -1387,7 +1391,7 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg }) } - func animateOut(completion: (() -> Void)? = nil) { + public func animateOut(completion: (() -> Void)? = nil) { self.animatedOut = true let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY @@ -1399,7 +1403,7 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg }) } - func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { let contentOffset = scrollView.contentOffset let additionalTopHeight = max(0.0, -contentOffset.y) @@ -1408,7 +1412,7 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg } } - func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { + public func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { self.containerLayout = (layout, navigationBarHeight) var insets = layout.insets(options: [.statusBar, .input]) @@ -1846,7 +1850,7 @@ private class QrContentNode: ASDisplayNode, ContentNode { func update(theme: PresentationTheme, wallpaper: TelegramWallpaper, isDarkAppearance: Bool, selectedEmoticon: String?) { self.currentParams = (theme, wallpaper, isDarkAppearance, selectedEmoticon) - self.wallpaperBackgroundNode.update(wallpaper: wallpaper) + self.wallpaperBackgroundNode.update(wallpaper: wallpaper, animated: false) self.codeForegroundDimNode.alpha = isDarkAppearance ? 0.5 : 0.3 @@ -2181,7 +2185,7 @@ private class MessageContentNode: ASDisplayNode, ContentNode { func update(theme: PresentationTheme, wallpaper: TelegramWallpaper, isDarkAppearance: Bool, selectedEmoticon: String?) { self.currentParams = (theme, wallpaper, isDarkAppearance, selectedEmoticon) - self.wallpaperBackgroundNode.update(wallpaper: wallpaper) + self.wallpaperBackgroundNode.update(wallpaper: wallpaper, animated: false) self.linkBackgroundDimNode.alpha = isDarkAppearance ? 0.6 : 0.2 diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift index ea1badefeae..daac53664ac 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift @@ -570,6 +570,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { }, openNoAdsDemo: { }, displayGiveawayParticipationStatus: { _ in }, openPremiumStatusInfo: { _, _, _, _ in + }, openRecommendedChannelContextMenu: { _, _, _ in }, requestMessageUpdate: { _, _ in }, cancelInteractiveKeyboardGestures: { }, dismissTextInput: { @@ -663,7 +664,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { self.chatPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true, largeEmoji: presentationData.largeEmoji, chatBubbleCorners: presentationData.chatBubbleCorners) self.chatPresentationDataPromise.set(.single(self.chatPresentationData)) - self.backgroundNode.update(wallpaper: presentationData.chatWallpaper) + self.backgroundNode.update(wallpaper: presentationData.chatWallpaper, animated: false) self.backgroundNode.updateBubbleTheme(bubbleTheme: presentationData.theme, bubbleCorners: presentationData.chatBubbleCorners) self.panelBackgroundNode.updateColor(color: presentationData.theme.chat.inputPanel.panelBackgroundColor, transition: .immediate) @@ -1069,7 +1070,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { self?.presentController(c, .window(.root), a) }, dismissInput: { self?.view.endEditing(true) - }, contentContext: nil, progress: nil) + }, contentContext: nil, progress: nil, completion: nil) case .wallpaper: break case .theme: diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift index 664f73eab7c..b932696754d 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift @@ -69,7 +69,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) + 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) } return peer } @@ -117,7 +117,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.titleUpdated(title: new) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeAbout(prev, new): var peers = SimpleDictionary() var author: Peer? @@ -1496,33 +1496,62 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { var text: String = "" var entities: [MessageTextEntity] = [] - let rawText: PresentationStrings.FormattedString switch updatedValue { case .all: - rawText = self.presentationData.strings.Channel_AdminLog_ReactionsEnabled(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") + let rawText = self.presentationData.strings.Channel_AdminLog_ReactionsEnabled(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") + appendAttributedText(text: rawText, generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } else if index == 1 { + return [.Bold] + } + return [] + }, to: &text, entities: &entities) case let .limited(reactions): - let emojiString = reactions.compactMap({ reaction -> String? in - switch reaction { - case let .builtin(value): - return value - case .custom: - return nil + let authorTitle = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" + let rawText = self.presentationData.strings.Channel_AdminLog_AllowedReactionsUpdated(authorTitle, "") + var previousIndex = 0 + let nsText = rawText.string as NSString + for range in rawText.ranges.sorted(by: { $0.range.lowerBound < $1.range.lowerBound }) { + if range.range.lowerBound > previousIndex { + text.append(nsText.substring(with: NSRange(location: previousIndex, length: range.range.lowerBound - previousIndex))) + } + if range.index == 0 { + if let author { + entities.append(MessageTextEntity(range: (text as NSString).length ..< (text as NSString).length + (authorTitle as NSString).length, type: .TextMention(peerId: author.id))) + } + text.append(authorTitle) + } else if range.index == 1 { + for reaction in reactions { + let reactionText: String + switch reaction { + case let .builtin(value): + reactionText = value + text.append(reactionText) + case let .custom(fileId): + reactionText = "." + entities.append(MessageTextEntity(range: (text as NSString).length ..< (text as NSString).length + (reactionText as NSString).length, type: .CustomEmoji(stickerPack: nil, fileId: fileId))) + text.append(reactionText) + } + } } - }).joined(separator: ", ") - rawText = self.presentationData.strings.Channel_AdminLog_AllowedReactionsUpdated(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", emojiString) + previousIndex = range.range.upperBound + } + if nsText.length > previousIndex { + text.append(nsText.substring(with: NSRange(location: previousIndex, length: nsText.length - previousIndex))) + } case .empty: - rawText = self.presentationData.strings.Channel_AdminLog_ReactionsDisabled(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") + let rawText = self.presentationData.strings.Channel_AdminLog_ReactionsDisabled(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") + appendAttributedText(text: rawText, generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } else if index == 1 { + return [.Bold] + } + return [] + }, to: &text, entities: &entities) } - appendAttributedText(text: rawText, generateEntities: { index in - if index == 0, let author = author { - return [.TextMention(peerId: author.id)] - } else if index == 1 { - return [.Bold] - } - return [] - }, to: &text, entities: &entities) - let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) diff --git a/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift b/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift index 08f91020340..60617ab3856 100644 --- a/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift +++ b/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift @@ -19,10 +19,20 @@ import AnimationCache import MultiAnimationRenderer public struct ChatInterfaceHighlightedState: Equatable { + public struct Quote: Equatable { + public var string: String + public var offset: Int? + + public init(string: String, offset: Int?) { + self.string = string + self.offset = offset + } + } + public let messageStableId: UInt32 - public let quote: String? + public let quote: Quote? - public init(messageStableId: UInt32, quote: String?) { + public init(messageStableId: UInt32, quote: Quote?) { self.messageStableId = messageStableId self.quote = quote } @@ -74,11 +84,21 @@ public protocol ChatMessageTransitionProtocol: ASDisplayNode { } public struct NavigateToMessageParams { + public struct Quote { + public var string: String + public var offset: Int? + + public init(string: String, offset: Int?) { + self.string = string + self.offset = offset + } + } + public var timestamp: Double? - public var quote: String? + public var quote: Quote? public var progress: Promise? - public init(timestamp: Double?, quote: String?, progress: Promise? = nil) { + public init(timestamp: Double?, quote: Quote?, progress: Promise? = nil) { self.timestamp = timestamp self.quote = quote self.progress = progress @@ -95,7 +115,7 @@ public struct OpenMessageParams { } } -public final class ChatControllerInteraction { +public final class ChatControllerInteraction: ChatControllerInteractionProtocol { public enum OpenPeerSource { case `default` case reaction @@ -214,6 +234,7 @@ public final class ChatControllerInteraction { public let openNoAdsDemo: () -> Void public let displayGiveawayParticipationStatus: (EngineMessage.Id) -> Void public let openPremiumStatusInfo: (EnginePeer.Id, UIView, Int64?, PeerNameColor) -> Void + public let openRecommendedChannelContextMenu: (EnginePeer, UIView, ContextGesture?) -> Void public let requestMessageUpdate: (MessageId, Bool) -> Void public let cancelInteractiveKeyboardGestures: () -> Void @@ -240,6 +261,7 @@ public final class ChatControllerInteraction { public var updatedPresentationData: (initial: PresentationData, signal: Signal)? public let presentationContext: ChatPresentationContext public var playNextOutgoingGift: Bool = false + public var recommendedChannelsOpenUp: Bool = false public var enableFullTranslucency: Bool = true public init( @@ -336,6 +358,7 @@ public final class ChatControllerInteraction { openNoAdsDemo: @escaping () -> Void, displayGiveawayParticipationStatus: @escaping (EngineMessage.Id) -> Void, openPremiumStatusInfo: @escaping (EnginePeer.Id, UIView, Int64?, PeerNameColor) -> Void, + openRecommendedChannelContextMenu: @escaping (EnginePeer, UIView, ContextGesture?) -> Void, requestMessageUpdate: @escaping (MessageId, Bool) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, dismissTextInput: @escaping () -> Void, @@ -440,6 +463,7 @@ public final class ChatControllerInteraction { self.openNoAdsDemo = openNoAdsDemo self.displayGiveawayParticipationStatus = displayGiveawayParticipationStatus self.openPremiumStatusInfo = openPremiumStatusInfo + self.openRecommendedChannelContextMenu = openRecommendedChannelContextMenu self.requestMessageUpdate = requestMessageUpdate self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures self.dismissTextInput = dismissTextInput diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift index d407acf2d38..a17dc48b20e 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift @@ -890,13 +890,18 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { } } let remoteSignal: Signal<(items: [TelegramMediaFile], isFinalResult: Bool), NoError> + let remotePacksSignal: Signal<(sets: FoundStickerSets, isFinalResult: Bool), NoError> if hasPremium { remoteSignal = context.engine.stickers.searchEmoji(emojiString: Array(allEmoticons.keys)) + remotePacksSignal = .single((FoundStickerSets(), false)) |> then(context.engine.stickers.searchEmojiSetsRemotely(query: query) |> map { + ($0, true) + }) } else { remoteSignal = .single(([], true)) + remotePacksSignal = .single((FoundStickerSets(), true)) } - return remoteSignal - |> mapToSignal { foundEmoji -> Signal<[EmojiPagerContentComponent.ItemGroup], NoError> in + return combineLatest(remoteSignal, remotePacksSignal) + |> mapToSignal { foundEmoji, foundPacks -> Signal<[EmojiPagerContentComponent.ItemGroup], NoError> in if foundEmoji.items.isEmpty && !foundEmoji.isFinalResult { return .complete() } @@ -948,8 +953,9 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { if hasPremium { appendUnicodeEmoji() } - - return .single([EmojiPagerContentComponent.ItemGroup( + + var resultGroups: [EmojiPagerContentComponent.ItemGroup] = [] + resultGroups.append(EmojiPagerContentComponent.ItemGroup( supergroupId: "search", groupId: "search", title: nil, @@ -964,7 +970,59 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { headerItem: nil, fillWithLoadingPlaceholders: false, items: items - )]) + )) + + for (collectionId, info, _, _) in foundPacks.sets.infos { + if let info = info as? StickerPackCollectionInfo { + var topItems: [StickerPackItem] = [] + for e in foundPacks.sets.entries { + if let item = e.item as? StickerPackItem { + if e.index.collectionId == collectionId { + topItems.append(item) + } + } + } + + var groupItems: [EmojiPagerContentComponent.Item] = [] + for item in topItems { + var tintMode: EmojiPagerContentComponent.Item.TintMode = .none + if item.file.isCustomTemplateEmoji { + tintMode = .primary + } + + let animationData = EntityKeyboardAnimationData(file: item.file) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: item.file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + groupItems.append(resultItem) + } + + resultGroups.append(EmojiPagerContentComponent.ItemGroup( + supergroupId: AnyHashable(info.id), + groupId: AnyHashable(info.id), + title: info.title, + subtitle: nil, + actionButtonTitle: nil, + isFeatured: false, + isPremiumLocked: false, + isEmbedded: false, + hasClear: false, + collapsedLineCount: 3, + displayPremiumBadges: false, + headerItem: nil, + fillWithLoadingPlaceholders: false, + items: groupItems + )) + } + } + + return .single(resultGroups) } } diff --git a/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputMediaRecordingButton.swift b/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputMediaRecordingButton.swift index ca1a6f386e6..446862b82c4 100644 --- a/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputMediaRecordingButton.swift +++ b/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputMediaRecordingButton.swift @@ -205,6 +205,35 @@ public final class ChatTextInputMediaRecordingButton: TGModernConversationInputM private var micLevelDisposable: MetaDisposable? private weak var currentPresenter: UIView? + + public var hasShadow: Bool = false { + didSet { + self.updateShadow() + } + } + + public var hidesOnLock: Bool = false { + didSet { + if self.hidesOnLock { + self.setHidesPanelOnLock() + } + } + } + + private func updateShadow() { + if let view = self.animationView.view { + if self.hasShadow { + view.layer.shadowOffset = CGSize(width: 0.0, height: 0.0) + view.layer.shadowRadius = 2.0 + view.layer.shadowColor = UIColor.black.cgColor + view.layer.shadowOpacity = 0.35 + } else { + view.layer.shadowRadius = 0.0 + view.layer.shadowColor = UIColor.clear.cgColor + view.layer.shadowOpacity = 0.0 + } + } + } public var contentContainer: (UIView, CGRect)? { if let _ = self.currentPresenter { @@ -283,7 +312,8 @@ public final class ChatTextInputMediaRecordingButton: TGModernConversationInputM mediumBlobRange: (0.52, 0.87), bigBlobRange: (0.57, 1.00) ) - blobView.setColor(self.theme.chat.inputPanel.actionControlFillColor) + let theme = self.hidesOnLock ? defaultDarkColorPresentationTheme : self.theme + blobView.setColor(theme.chat.inputPanel.actionControlFillColor) self.micDecorationValue = blobView return blobView } @@ -353,13 +383,14 @@ public final class ChatTextInputMediaRecordingButton: TGModernConversationInputM private func updateAnimation(previousMode: ChatTextInputMediaRecordingButtonMode) { let image: UIImage? + let theme = self.hidesOnLock ? defaultDarkColorPresentationTheme : self.theme switch self.mode { case .audio: - self.icon = PresentationResourcesChat.chatInputPanelVoiceActiveButtonImage(self.theme) - image = PresentationResourcesChat.chatInputPanelVoiceButtonImage(self.theme) + self.icon = PresentationResourcesChat.chatInputPanelVoiceActiveButtonImage(theme) + image = PresentationResourcesChat.chatInputPanelVoiceButtonImage(theme) case .video: - self.icon = PresentationResourcesChat.chatInputPanelVideoActiveButtonImage(self.theme) - image = PresentationResourcesChat.chatInputPanelVoiceButtonImage(self.theme) + self.icon = PresentationResourcesChat.chatInputPanelVideoActiveButtonImage(theme) + image = PresentationResourcesChat.chatInputPanelVoiceButtonImage(theme) } let size = self.bounds.size @@ -394,6 +425,7 @@ public final class ChatTextInputMediaRecordingButton: TGModernConversationInputM view.isUserInteractionEnabled = false if view.superview == nil { self.insertSubview(view, at: 0) + self.updateShadow() } view.frame = animationFrame diff --git a/submodules/TelegramUI/Components/DustEffect/BUILD b/submodules/TelegramUI/Components/DustEffect/BUILD new file mode 100644 index 00000000000..5444d4eeb17 --- /dev/null +++ b/submodules/TelegramUI/Components/DustEffect/BUILD @@ -0,0 +1,63 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +load( + "@build_bazel_rules_apple//apple:resources.bzl", + "apple_resource_bundle", + "apple_resource_group", +) +load("//build-system/bazel-utils:plist_fragment.bzl", + "plist_fragment", +) + +filegroup( + name = "DustEffectMetalSources", + srcs = glob([ + "Metal/**/*.metal", + ]), + visibility = ["//visibility:public"], +) + +plist_fragment( + name = "DustEffectMetalSourcesBundleInfoPlist", + extension = "plist", + template = + """ + CFBundleIdentifier + org.telegram.DustEffectMetalSources + CFBundleDevelopmentRegion + en + CFBundleName + DustEffect + """ +) + +apple_resource_bundle( + name = "DustEffectMetalSourcesBundle", + infoplists = [ + ":DustEffectMetalSourcesBundleInfoPlist", + ], + resources = [ + ":DustEffectMetalSources", + ], +) + +swift_library( + name = "DustEffect", + module_name = "DustEffect", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + data = [ + ":DustEffectMetalSourcesBundle", + ], + deps = [ + "//submodules/Display", + "//submodules/MetalEngine", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/DustEffect/Metal/DustEffectShaders.metal b/submodules/TelegramUI/Components/DustEffect/Metal/DustEffectShaders.metal new file mode 100644 index 00000000000..152adb1c90e --- /dev/null +++ b/submodules/TelegramUI/Components/DustEffect/Metal/DustEffectShaders.metal @@ -0,0 +1,147 @@ +#include + +#include "loki_header.metal" + +using namespace metal; + +struct Rectangle { + float2 origin; + float2 size; +}; + +constant static float2 quadVertices[6] = { + float2(0.0, 0.0), + float2(1.0, 0.0), + float2(0.0, 1.0), + float2(1.0, 0.0), + float2(0.0, 1.0), + float2(1.0, 1.0) +}; + +struct QuadVertexOut { + float4 position [[position]]; + float2 uv; + float alpha; +}; + +float2 mapLocalToScreenCoordinates(const device Rectangle &rect, const device float2 &size, float2 position) { + float2 result = float2(rect.origin.x + position.x / size.x * rect.size.x, rect.origin.y + position.y / size.y * rect.size.y); + result.x = -1.0 + result.x * 2.0; + result.y = -1.0 + result.y * 2.0; + + return result; +} + +struct Particle { + packed_float2 offsetFromBasePosition; + packed_float2 velocity; + float lifetime; +}; + +kernel void dustEffectInitializeParticle( + device Particle *particles [[ buffer(0) ]], + uint gid [[ thread_position_in_grid ]] +) { + Loki rng = Loki(gid); + + Particle particle; + particle.offsetFromBasePosition = packed_float2(0.0, 0.0); + + float direction = rng.rand() * (3.14159265 * 2.0); + float velocity = (0.1 + rng.rand() * (0.2 - 0.1)) * 420.0; + particle.velocity = packed_float2(cos(direction) * velocity, sin(direction) * velocity); + + particle.lifetime = 0.7 + rng.rand() * (1.5 - 0.7); + + particles[gid] = particle; +} + +float particleEaseInWindowFunction(float t) { + return t; +} + +float particleEaseInValueAt(float fraction, float t) { + float windowSize = 0.8; + + float effectiveT = t; + float windowStartOffset = -windowSize; + float windowEndOffset = 1.0; + + float windowPosition = (1.0 - fraction) * windowStartOffset + fraction * windowEndOffset; + float windowT = max(0.0, min(windowSize, effectiveT - windowPosition)) / windowSize; + float localT = 1.0 - particleEaseInWindowFunction(windowT); + + return localT; +} + +kernel void dustEffectUpdateParticle( + device Particle *particles [[ buffer(0) ]], + const device uint2 &size [[ buffer(1) ]], + const device float &phase [[ buffer(2) ]], + const device float &timeStep [[ buffer(3) ]], + uint gid [[ thread_position_in_grid ]] +) { + uint count = size.x * size.y; + if (gid >= count) { + return; + } + + constexpr float easeInDuration = 0.8; + float effectFraction = max(0.0, min(easeInDuration, phase)) / easeInDuration; + + uint particleX = gid % size.x; + float particleXFraction = float(particleX) / float(size.x); + float particleFraction = particleEaseInValueAt(effectFraction, particleXFraction); + + //Loki rng = Loki(gid, uint(phase * timeStep)); + //float2 offsetNorm = float2(1.0, 1.0) * 10.0 * timeStep; + + Particle particle = particles[gid]; + particle.offsetFromBasePosition += (particle.velocity * timeStep) * particleFraction; + //particle.velocity += ((-offsetNorm) * 0.5 + float2(rng.rand(), rng.rand()) * offsetNorm) * particleFraction; + //particle.velocity = particle.velocity * (1.0 - particleFraction) + particle.velocity * 1.001 * particleFraction; + particle.velocity += float2(0.0, timeStep * 120.0) * particleFraction; + particle.lifetime = max(0.0, particle.lifetime - timeStep * particleFraction); + particles[gid] = particle; +} + +vertex QuadVertexOut dustEffectVertex( + const device Rectangle &rect [[ buffer(0) ]], + const device float2 &size [[ buffer(1) ]], + const device uint2 &particleResolution [[ buffer(2) ]], + const device Particle *particles [[ buffer(3) ]], + unsigned int vid [[ vertex_id ]], + unsigned int particleId [[ instance_id ]] +) { + QuadVertexOut out; + + float2 quadVertex = quadVertices[vid]; + + uint particleIndexX = particleId % particleResolution.x; + uint particleIndexY = particleId / particleResolution.x; + + Particle particle = particles[particleId]; + + float2 particleSize = size / float2(particleResolution); + + float2 topLeftPosition = float2(float(particleIndexX) * particleSize.x, float(particleIndexY) * particleSize.y); + out.uv = (topLeftPosition + quadVertex * particleSize) / size; + + topLeftPosition += particle.offsetFromBasePosition; + float2 position = topLeftPosition + quadVertex * particleSize; + + out.position = float4(mapLocalToScreenCoordinates(rect, size, position), 0.0, 1.0); + out.alpha = max(0.0, min(0.3, particle.lifetime) / 0.3); + + return out; +} + +fragment half4 dustEffectFragment( + QuadVertexOut in [[stage_in]], + texture2d inTexture [[ texture(0) ]] +) { + constexpr sampler sampler(coord::normalized, address::clamp_to_edge, filter::linear); + + half4 color = inTexture.sample(sampler, float2(in.uv.x, 1.0 - in.uv.y)); + return color * in.alpha; +} diff --git a/submodules/TelegramUI/Components/DustEffect/Metal/loki.metal b/submodules/TelegramUI/Components/DustEffect/Metal/loki.metal new file mode 100755 index 00000000000..45241612c1f --- /dev/null +++ b/submodules/TelegramUI/Components/DustEffect/Metal/loki.metal @@ -0,0 +1,54 @@ +#include +#include "loki_header.metal" + +unsigned Loki::TausStep(const unsigned z, const int s1, const int s2, const int s3, const unsigned M) +{ + unsigned b=(((z << s1) ^ z) >> s2); + return (((z & M) << s3) ^ b); +} + +thread Loki::Loki(const unsigned seed1, const unsigned seed2, const unsigned seed3) { + unsigned seed = seed1 * 1099087573UL; + unsigned seedb = seed2 * 1099087573UL; + unsigned seedc = seed3 * 1099087573UL; + + // Round 1: Randomise seed + unsigned z1 = TausStep(seed,13,19,12,429496729UL); + unsigned z2 = TausStep(seed,2,25,4,4294967288UL); + unsigned z3 = TausStep(seed,3,11,17,429496280UL); + unsigned z4 = (1664525*seed + 1013904223UL); + + // Round 2: Randomise seed again using second seed + unsigned r1 = (z1^z2^z3^z4^seedb); + + z1 = TausStep(r1,13,19,12,429496729UL); + z2 = TausStep(r1,2,25,4,4294967288UL); + z3 = TausStep(r1,3,11,17,429496280UL); + z4 = (1664525*r1 + 1013904223UL); + + // Round 3: Randomise seed again using third seed + r1 = (z1^z2^z3^z4^seedc); + + z1 = TausStep(r1,13,19,12,429496729UL); + z2 = TausStep(r1,2,25,4,4294967288UL); + z3 = TausStep(r1,3,11,17,429496280UL); + z4 = (1664525*r1 + 1013904223UL); + + this->seed = (z1^z2^z3^z4) * 2.3283064365387e-10; +} + +thread float Loki::rand() { + unsigned hashed_seed = this->seed * 1099087573UL; + + unsigned z1 = TausStep(hashed_seed,13,19,12,429496729UL); + unsigned z2 = TausStep(hashed_seed,2,25,4,4294967288UL); + unsigned z3 = TausStep(hashed_seed,3,11,17,429496280UL); + unsigned z4 = (1664525*hashed_seed + 1013904223UL); + + thread float old_seed = this->seed; + + this->seed = (z1^z2^z3^z4) * 2.3283064365387e-10; + + return old_seed; +} + diff --git a/submodules/TelegramUI/Components/DustEffect/Metal/loki_header.metal b/submodules/TelegramUI/Components/DustEffect/Metal/loki_header.metal new file mode 100755 index 00000000000..49c358ad776 --- /dev/null +++ b/submodules/TelegramUI/Components/DustEffect/Metal/loki_header.metal @@ -0,0 +1,45 @@ +/* + * Loki Random Number Generator + * Copyright (c) 2017 Youssef Victor All rights reserved. + * + * Function Result + * ------------------------------------------------------------------ + * + * TausStep Combined Tausworthe Generator or + * Linear Feedback Shift Register (LFSR) + * random number generator. This is a + * helper method for rng, which uses + * a hybrid approach combining LFSR with + * a Linear Congruential Generator (LCG) + * in order to produce random numbers with + * periods of well over 2^121 + * + * rand A pseudo-random number based on the + * method outlined in "Efficient + * pseudo-random number generation + * for monte-carlo simulations using + * graphic processors" by Siddhant + * Mohanty et al 2012. + * + */ + +#include +using namespace metal; + +#ifndef LOKI +#define LOKI + + +class Loki { +private: + thread float seed; + unsigned TausStep(const unsigned z, const int s1, const int s2, const int s3, const unsigned M); + +public: + thread Loki(const unsigned seed1, const unsigned seed2 = 1, const unsigned seed3 = 1); + + thread float rand(); +}; + +#endif + diff --git a/submodules/TelegramUI/Components/DustEffect/Sources/DustEffectLayer.swift b/submodules/TelegramUI/Components/DustEffect/Sources/DustEffectLayer.swift new file mode 100644 index 00000000000..c907e93c699 --- /dev/null +++ b/submodules/TelegramUI/Components/DustEffect/Sources/DustEffectLayer.swift @@ -0,0 +1,329 @@ +import Foundation +import UIKit +import Display +import MetalEngine +import MetalKit + +private final class BundleMarker: NSObject { +} + +private var metalLibraryValue: MTLLibrary? +func metalLibrary(device: MTLDevice) -> MTLLibrary? { + if let metalLibraryValue { + return metalLibraryValue + } + + let mainBundle = Bundle(for: BundleMarker.self) + guard let path = mainBundle.path(forResource: "DustEffectMetalSourcesBundle", ofType: "bundle") else { + return nil + } + guard let bundle = Bundle(path: path) else { + return nil + } + guard let library = try? device.makeDefaultLibrary(bundle: bundle) else { + return nil + } + + metalLibraryValue = library + return library +} + +public final class DustEffectLayer: MetalEngineSubjectLayer, MetalEngineSubject { + public var internalData: MetalEngineSubjectInternalData? + + private final class Item { + let frame: CGRect + let texture: MTLTexture + + var phase: Float = 0 + var particleBufferIsInitialized: Bool = false + var particleBuffer: SharedBuffer? + + init?(frame: CGRect, image: UIImage) { + self.frame = frame + + guard let cgImage = image.cgImage, let texture = try? MTKTextureLoader(device: MetalEngine.shared.device).newTexture(cgImage: cgImage, options: [.SRGB: false as NSNumber]) else { + return nil + } + self.texture = texture + } + } + + private final class RenderState: RenderToLayerState { + let pipelineState: MTLRenderPipelineState + + init?(device: MTLDevice) { + guard let library = metalLibrary(device: device) else { + return nil + } + guard let vertexFunction = library.makeFunction(name: "dustEffectVertex"), let fragmentFunction = library.makeFunction(name: "dustEffectFragment") else { + return nil + } + + let pipelineDescriptor = MTLRenderPipelineDescriptor() + pipelineDescriptor.vertexFunction = vertexFunction + pipelineDescriptor.fragmentFunction = fragmentFunction + pipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm + pipelineDescriptor.colorAttachments[0].isBlendingEnabled = true + pipelineDescriptor.colorAttachments[0].rgbBlendOperation = .add + pipelineDescriptor.colorAttachments[0].alphaBlendOperation = .add + pipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = .one + pipelineDescriptor.colorAttachments[0].sourceAlphaBlendFactor = .one + pipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha + pipelineDescriptor.colorAttachments[0].destinationAlphaBlendFactor = .one + guard let pipelineState = try? device.makeRenderPipelineState(descriptor: pipelineDescriptor) else { + return nil + } + self.pipelineState = pipelineState + } + } + + final class DustComputeState: ComputeState { + let computePipelineStateInitializeParticle: MTLComputePipelineState + let computePipelineStateUpdateParticle: MTLComputePipelineState + + required init?(device: MTLDevice) { + guard let library = metalLibrary(device: device) else { + return nil + } + + guard let functionDustEffectInitializeParticle = library.makeFunction(name: "dustEffectInitializeParticle") else { + return nil + } + guard let computePipelineStateInitializeParticle = try? device.makeComputePipelineState(function: functionDustEffectInitializeParticle) else { + return nil + } + self.computePipelineStateInitializeParticle = computePipelineStateInitializeParticle + + guard let functionDustEffectUpdateParticle = library.makeFunction(name: "dustEffectUpdateParticle") else { + return nil + } + guard let computePipelineStateUpdateParticle = try? device.makeComputePipelineState(function: functionDustEffectUpdateParticle) else { + return nil + } + + self.computePipelineStateUpdateParticle = computePipelineStateUpdateParticle + } + } + + private var updateLink: SharedDisplayLinkDriver.Link? + private var items: [Item] = [] + private var lastTimeStep: Double = 0.0 + + public var becameEmpty: (() -> Void)? + + override public init() { + super.init() + + self.isOpaque = false + self.backgroundColor = nil + + self.didEnterHierarchy = { [weak self] in + guard let self else { + return + } + self.updateNeedsAnimation() + } + self.didExitHierarchy = { [weak self] in + guard let self else { + return + } + self.updateNeedsAnimation() + } + } + + override public init(layer: Any) { + super.init(layer: layer) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private var lastUpdateTimestamp: Double? + + private func updateItems(deltaTime: Double) { + let timestamp = CACurrentMediaTime() + let localDeltaTime: Double + if let lastUpdateTimestamp = self.lastUpdateTimestamp { + localDeltaTime = timestamp - lastUpdateTimestamp + } else { + localDeltaTime = 0.0 + } + self.lastUpdateTimestamp = timestamp + + let deltaTimeValue: Double + if localDeltaTime <= 0.001 || localDeltaTime >= 0.2 { + deltaTimeValue = deltaTime + } else { + deltaTimeValue = localDeltaTime + } + + self.lastTimeStep = deltaTimeValue + //print("updateItems: \(deltaTime), localDeltaTime: \(localDeltaTime)") + + var didRemoveItems = false + for i in (0 ..< self.items.count).reversed() { + self.items[i].phase += Float(deltaTimeValue) / Float(UIView.animationDurationFactor()) + + if self.items[i].phase >= 4.0 { + self.items.remove(at: i) + didRemoveItems = true + } + } + self.updateNeedsAnimation() + + if didRemoveItems && self.items.isEmpty { + self.becameEmpty?() + } + } + + private func updateNeedsAnimation() { + if !self.items.isEmpty && self.isInHierarchy { + if self.updateLink == nil { + self.updateLink = SharedDisplayLinkDriver.shared.add(framesPerSecond: .max, { [weak self] deltaTime in + guard let self else { + return + } + self.updateItems(deltaTime: deltaTime) + self.setNeedsUpdate() + }) + } + } else { + if self.updateLink != nil { + self.updateLink = nil + } + } + } + + public func addItem(frame: CGRect, image: UIImage) { + if let item = Item(frame: frame, image: image) { + self.items.append(item) + self.updateNeedsAnimation() + self.setNeedsUpdate() + } + } + + public func update(context: MetalEngineSubjectContext) { + if self.bounds.isEmpty { + return + } + + let containerSize = self.bounds.size + + for item in self.items { + var itemFrame = item.frame + itemFrame.origin.y = containerSize.height - itemFrame.maxY + + let particleColumnCount = Int(itemFrame.width) + let particleRowCount = Int(itemFrame.height) + let particleCount = particleColumnCount * particleRowCount + + if item.particleBuffer == nil { + if let particleBuffer = MetalEngine.shared.sharedBuffer(spec: BufferSpec(length: particleCount * 4 * (4 + 1))) { + item.particleBuffer = particleBuffer + + /*let particles = particleBuffer.buffer.contents().assumingMemoryBound(to: Float.self) + for i in 0 ..< particleCount { + particles[i * 5 + 0] = 0.0; + particles[i * 5 + 1] = 0.0; + + let direction = Float.random(in: 0.0 ..< Float.pi * 2.0) + let velocity = Float.random(in: 0.1 ... 0.2) * 420.0 + particles[i * 5 + 2] = cos(direction) * velocity + particles[i * 5 + 3] = sin(direction) * velocity + + particles[i * 5 + 4] = Float.random(in: 0.7 ... 1.5) + }*/ + } + } + } + + let lastTimeStep = self.lastTimeStep + self.lastTimeStep = 0.0 + + let _ = context.compute(state: DustComputeState.self, commands: { [weak self] commandBuffer, state in + guard let self else { + return + } + guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { + return + } + + for item in self.items { + guard let particleBuffer = item.particleBuffer else { + continue + } + + let itemFrame = item.frame + let particleColumnCount = Int(itemFrame.width) + let particleRowCount = Int(itemFrame.height) + + let threadgroupSize = MTLSize(width: 32, height: 1, depth: 1) + let threadgroupCount = MTLSize(width: (particleRowCount * particleColumnCount + threadgroupSize.width - 1) / threadgroupSize.width, height: 1, depth: 1) + + computeEncoder.setBuffer(particleBuffer.buffer, offset: 0, index: 0) + + if !item.particleBufferIsInitialized { + item.particleBufferIsInitialized = true + computeEncoder.setComputePipelineState(state.computePipelineStateInitializeParticle) + computeEncoder.dispatchThreadgroups(threadgroupCount, threadsPerThreadgroup: threadgroupSize) + } + + if lastTimeStep != 0.0 { + computeEncoder.setComputePipelineState(state.computePipelineStateUpdateParticle) + var particleCount = SIMD2(UInt32(particleColumnCount), UInt32(particleRowCount)) + computeEncoder.setBytes(&particleCount, length: 4 * 2, index: 1) + var phase = item.phase + computeEncoder.setBytes(&phase, length: 4, index: 2) + var timeStep: Float = Float(lastTimeStep) / Float(UIView.animationDurationFactor()) + computeEncoder.setBytes(&timeStep, length: 4, index: 3) + computeEncoder.dispatchThreadgroups(threadgroupCount, threadsPerThreadgroup: threadgroupSize) + } + } + + computeEncoder.endEncoding() + }) + + context.renderToLayer(spec: RenderLayerSpec(size: RenderSize(width: Int(self.bounds.width * 3.0), height: Int(self.bounds.height * 3.0))), state: RenderState.self, layer: self, commands: { [weak self] encoder, placement in + guard let self else { + return + } + + for item in self.items { + guard let particleBuffer = item.particleBuffer else { + continue + } + + var itemFrame = item.frame + itemFrame.origin.y = containerSize.height - itemFrame.maxY + + let particleColumnCount = Int(itemFrame.width) + let particleRowCount = Int(itemFrame.height) + let particleCount = particleColumnCount * particleRowCount + + var effectiveRect = placement.effectiveRect + effectiveRect.origin.x += itemFrame.minX / containerSize.width * effectiveRect.width + effectiveRect.origin.y += itemFrame.minY / containerSize.height * effectiveRect.height + effectiveRect.size.width = itemFrame.width / containerSize.width * effectiveRect.width + effectiveRect.size.height = itemFrame.height / containerSize.height * effectiveRect.height + + var rect = SIMD4(Float(effectiveRect.minX), Float(effectiveRect.minY), Float(effectiveRect.width), Float(effectiveRect.height)) + encoder.setVertexBytes(&rect, length: 4 * 4, index: 0) + + var size = SIMD2(Float(itemFrame.width), Float(itemFrame.height)) + encoder.setVertexBytes(&size, length: 4 * 2, index: 1) + + var particleResolution = SIMD2(UInt32(particleColumnCount), UInt32(particleRowCount)) + encoder.setVertexBytes(&particleResolution, length: 4 * 2, index: 2) + + encoder.setVertexBuffer(particleBuffer.buffer, offset: 0, index: 3) + + encoder.setFragmentTexture(item.texture, index: 0) + + encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6) + encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6, instanceCount: particleCount) + } + }) + } +} diff --git a/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift b/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift index 800502be734..e204821d544 100644 --- a/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift +++ b/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift @@ -240,6 +240,8 @@ public final class EmojiStatusComponent: Component { var emojiLoopMode: LoopMode? var emojiSize = CGSize() + var iconTintColor: UIColor? + self.isUserInteractionEnabled = component.action != nil //let previousContent = self.component?.content @@ -248,19 +250,25 @@ public final class EmojiStatusComponent: Component { case .none: iconImage = nil case let .premium(color): - if let sourceImage = UIImage(bundleImageName: "Chat/Input/Media/EntityInputPremiumIcon") { - iconImage = generateImage(sourceImage.size, contextGenerator: { size, context in - if let cgImage = sourceImage.cgImage { - context.clear(CGRect(origin: CGPoint(), size: size)) - let imageSize = CGSize(width: sourceImage.size.width - 8.0, height: sourceImage.size.height - 8.0) - context.clip(to: CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floor((size.height - imageSize.height) / 2.0)), size: imageSize), mask: cgImage) - - context.setFillColor(color.cgColor) - context.fill(CGRect(origin: CGPoint(), size: size)) - } - }, opaque: false) + iconTintColor = color + + if case .premium = self.component?.content, let image = self.iconView?.image { + iconImage = image } else { - iconImage = nil + if let sourceImage = UIImage(bundleImageName: "Chat/Input/Media/EntityInputPremiumIcon") { + iconImage = generateImage(sourceImage.size, contextGenerator: { size, context in + if let cgImage = sourceImage.cgImage { + context.clear(CGRect(origin: CGPoint(), size: size)) + let imageSize = CGSize(width: sourceImage.size.width - 8.0, height: sourceImage.size.height - 8.0) + context.clip(to: CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floor((size.height - imageSize.height) / 2.0)), size: imageSize), mask: cgImage) + + context.setFillColor(UIColor.white.cgColor) + context.fill(CGRect(origin: CGPoint(), size: size)) + } + }, opaque: false)?.withRenderingMode(.alwaysTemplate) + } else { + iconImage = nil + } } case let .topic(title, color, realSize): let colors = topicIconColors(for: color) @@ -402,7 +410,19 @@ public final class EmojiStatusComponent: Component { iconView.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.5) } } - iconView.image = iconImage + if iconView.image !== iconImage { + iconView.image = iconImage + } + + if let iconTintColor { + if transition.animation.isImmediate { + iconView.tintColor = iconTintColor + } else { + transition.setTintColor(view: iconView, color: iconTintColor) + } + } else { + iconView.tintColor = nil + } var useFit = false switch component.content { @@ -504,12 +524,11 @@ public final class EmojiStatusComponent: Component { } } } + if accentTint { - animationLayer.contentTintColor = emojiThemeColor - animationLayer.dynamicColor = emojiThemeColor + animationLayer.updateTintColor(contentTintColor: emojiThemeColor, dynamicColor: emojiThemeColor, transition: transition) } else { - animationLayer.contentTintColor = nil - animationLayer.dynamicColor = nil + animationLayer.updateTintColor(contentTintColor: nil, dynamicColor: nil, transition: transition) } animationLayer.frame = CGRect(origin: CGPoint(), size: size) diff --git a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift index 347d40036f0..8df436290e2 100644 --- a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift +++ b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift @@ -65,6 +65,7 @@ public final class EmojiStatusSelectionComponent: Component { public let backgroundColor: UIColor public let separatorColor: UIColor public let hideTopPanel: Bool + public let disableTopPanel: Bool public let hideTopPanelUpdated: (Bool, Transition) -> Void public init( @@ -75,6 +76,7 @@ public final class EmojiStatusSelectionComponent: Component { backgroundColor: UIColor, separatorColor: UIColor, hideTopPanel: Bool, + disableTopPanel: Bool, hideTopPanelUpdated: @escaping (Bool, Transition) -> Void ) { self.theme = theme @@ -84,6 +86,7 @@ public final class EmojiStatusSelectionComponent: Component { self.backgroundColor = backgroundColor self.separatorColor = separatorColor self.hideTopPanel = hideTopPanel + self.disableTopPanel = disableTopPanel self.hideTopPanelUpdated = hideTopPanelUpdated } @@ -109,6 +112,9 @@ public final class EmojiStatusSelectionComponent: Component { if lhs.hideTopPanel != rhs.hideTopPanel { return false } + if lhs.disableTopPanel != rhs.disableTopPanel { + return false + } return true } @@ -193,7 +199,8 @@ public final class EmojiStatusSelectionComponent: Component { displayBottomPanel: false, isExpanded: false, clipContentToTopPanel: false, - useExternalSearchContainer: false + useExternalSearchContainer: false, + hidePanels: component.disableTopPanel )), environment: {}, containerSize: availableSize @@ -984,6 +991,7 @@ public final class EmojiStatusSelectionController: ViewController { backgroundColor: listBackgroundColor, separatorColor: separatorColor, hideTopPanel: self.isReactionSearchActive, + disableTopPanel: false, hideTopPanelUpdated: { [weak self] hideTopPanel, transition in guard let strongSelf = self else { return diff --git a/submodules/TelegramUI/Components/EmojiTextAttachmentView/BUILD b/submodules/TelegramUI/Components/EmojiTextAttachmentView/BUILD index bff8f466358..294e001b6f7 100644 --- a/submodules/TelegramUI/Components/EmojiTextAttachmentView/BUILD +++ b/submodules/TelegramUI/Components/EmojiTextAttachmentView/BUILD @@ -26,6 +26,8 @@ swift_library( "//submodules/ShimmerEffect:ShimmerEffect", "//submodules/TelegramUIPreferences", "//submodules/TelegramUI/Components/Utils/GenerateStickerPlaceholderImage", + "//submodules/UIKitRuntimeUtils", + "//submodules/ComponentFlow", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift b/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift index 018d5d46469..f4f10117086 100644 --- a/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift +++ b/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift @@ -17,6 +17,8 @@ import ShimmerEffect import TextFormat import TelegramUIPreferences import GenerateStickerPlaceholderImage +import UIKitRuntimeUtils +import ComponentFlow public func generateTopicIcon(title: String, backgroundColors: [UIColor], strokeColors: [UIColor], size: CGSize) -> UIImage? { let realSize = size @@ -244,17 +246,27 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { private var fetchDisposable: Disposable? private var loadDisposable: Disposable? + private var _contentTintColor: UIColor? public var contentTintColor: UIColor? { - didSet { - if self.contentTintColor != oldValue { + get { + return self._contentTintColor + } + set(value) { + if self._contentTintColor != value { + self._contentTintColor = value self.updateTintColor() } } } + private var _dynamicColor: UIColor? public var dynamicColor: UIColor? { - didSet { - if self.dynamicColor != oldValue { + get { + return self._dynamicColor + } + set(value) { + if self._dynamicColor != value { + self._dynamicColor = value self.updateTintColor() } } @@ -296,7 +308,7 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { self.renderer = renderer self.unique = unique self.placeholderColor = placeholderColor - self.dynamicColor = dynamicColor + self._dynamicColor = dynamicColor self.loopCount = loopCount let scale = min(2.0, UIScreenScale) @@ -348,6 +360,40 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { return nullAction } + public func updateTintColor(contentTintColor: UIColor?, dynamicColor: UIColor?, transition: Transition) { + self._contentTintColor = contentTintColor + self._dynamicColor = dynamicColor + + if !self.isDisplayingPlaceholder { + var customColor = self.contentTintColor + if let file = self.file { + if file.isCustomTemplateEmoji { + customColor = self.dynamicColor + } + } + + if customColor != nil { + if self.layerTintColor == nil { + setLayerContentsMaskMode(self, true) + } + } else { + if self.layerTintColor != nil { + setLayerContentsMaskMode(self, false) + } + } + if let customColor { + transition.setTintColor(layer: self, color: customColor) + } else { + self.layerTintColor = nil + } + } else { + if self.layerTintColor != nil { + setLayerContentsMaskMode(self, false) + } + self.layerTintColor = nil + } + } + private func updateTintColor() { if !self.isDisplayingPlaceholder { var customColor = self.contentTintColor @@ -357,8 +403,20 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { } } + if customColor != nil { + if self.layerTintColor == nil { + setLayerContentsMaskMode(self, true) + } + } else { + if self.layerTintColor != nil { + setLayerContentsMaskMode(self, false) + } + } self.layerTintColor = customColor?.cgColor } else { + if self.layerTintColor != nil { + setLayerContentsMaskMode(self, false) + } self.layerTintColor = nil } } diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index 131a3b47845..7573cf2a5c8 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -167,7 +167,7 @@ private final class WarpView: UIView { } } -public struct EmojiComponentReactionItem { +public struct EmojiComponentReactionItem: Equatable { public var reaction: MessageReaction.Reaction public var file: TelegramMediaFile @@ -2327,17 +2327,20 @@ public final class EmojiPagerContentComponent: Component { } public struct CustomLayout: Equatable { + public var topPanelAlwaysHidden: Bool public var itemsPerRow: Int public var itemSize: CGFloat public var sideInset: CGFloat public var itemSpacing: CGFloat public init( + topPanelAlwaysHidden: Bool, itemsPerRow: Int, itemSize: CGFloat, sideInset: CGFloat, itemSpacing: CGFloat ) { + self.topPanelAlwaysHidden = topPanelAlwaysHidden self.itemsPerRow = itemsPerRow self.itemSize = itemSize self.sideInset = sideInset @@ -2729,6 +2732,7 @@ public final class EmojiPagerContentComponent: Component { public let emptySearchResults: EmptySearchResults? public let enableLongPress: Bool public let selectedItems: Set + public let customTintColor: UIColor? public init( id: AnyHashable, @@ -2751,7 +2755,8 @@ public final class EmojiPagerContentComponent: Component { searchIsPlaceholderOnly: Bool, emptySearchResults: EmptySearchResults?, enableLongPress: Bool, - selectedItems: Set + selectedItems: Set, + customTintColor: UIColor? ) { self.id = id self.context = context @@ -2774,6 +2779,7 @@ public final class EmojiPagerContentComponent: Component { self.emptySearchResults = emptySearchResults self.enableLongPress = enableLongPress self.selectedItems = selectedItems + self.customTintColor = customTintColor } public func withUpdatedItemGroups(panelItemGroups: [ItemGroup], contentItemGroups: [ItemGroup], itemContentUniqueId: ContentId?, emptySearchResults: EmptySearchResults?, searchState: SearchState) -> EmojiPagerContentComponent { @@ -2798,7 +2804,62 @@ public final class EmojiPagerContentComponent: Component { searchIsPlaceholderOnly: self.searchIsPlaceholderOnly, emptySearchResults: emptySearchResults, enableLongPress: self.enableLongPress, - selectedItems: self.selectedItems + selectedItems: self.selectedItems, + customTintColor: self.customTintColor + ) + } + + public func withSelectedItems(_ selectedItems: Set) -> EmojiPagerContentComponent { + return EmojiPagerContentComponent( + id: self.id, + context: self.context, + avatarPeer: self.avatarPeer, + animationCache: self.animationCache, + animationRenderer: self.animationRenderer, + inputInteractionHolder: self.inputInteractionHolder, + panelItemGroups: panelItemGroups, + contentItemGroups: contentItemGroups, + itemLayoutType: self.itemLayoutType, + itemContentUniqueId: itemContentUniqueId, + searchState: searchState, + warpContentsOnEdges: self.warpContentsOnEdges, + hideBackground: self.hideBackground, + displaySearchWithPlaceholder: self.displaySearchWithPlaceholder, + searchCategories: self.searchCategories, + searchInitiallyHidden: self.searchInitiallyHidden, + searchAlwaysActive: self.searchAlwaysActive, + searchIsPlaceholderOnly: self.searchIsPlaceholderOnly, + emptySearchResults: emptySearchResults, + enableLongPress: self.enableLongPress, + selectedItems: selectedItems, + customTintColor: self.customTintColor + ) + } + + public func withCustomTintColor(_ customTintColor: UIColor?) -> EmojiPagerContentComponent { + return EmojiPagerContentComponent( + id: self.id, + context: self.context, + avatarPeer: self.avatarPeer, + animationCache: self.animationCache, + animationRenderer: self.animationRenderer, + inputInteractionHolder: self.inputInteractionHolder, + panelItemGroups: panelItemGroups, + contentItemGroups: contentItemGroups, + itemLayoutType: self.itemLayoutType, + itemContentUniqueId: itemContentUniqueId, + searchState: searchState, + warpContentsOnEdges: self.warpContentsOnEdges, + hideBackground: self.hideBackground, + displaySearchWithPlaceholder: self.displaySearchWithPlaceholder, + searchCategories: self.searchCategories, + searchInitiallyHidden: self.searchInitiallyHidden, + searchAlwaysActive: self.searchAlwaysActive, + searchIsPlaceholderOnly: self.searchIsPlaceholderOnly, + emptySearchResults: emptySearchResults, + enableLongPress: self.enableLongPress, + selectedItems: self.selectedItems, + customTintColor: customTintColor ) } @@ -2869,6 +2930,9 @@ public final class EmojiPagerContentComponent: Component { if lhs.selectedItems != rhs.selectedItems { return false } + if lhs.customTintColor != rhs.customTintColor { + return false + } return true } @@ -3036,7 +3100,8 @@ public final class EmojiPagerContentComponent: Component { var verticalGroupOrigin: CGFloat = self.itemInsets.top self.itemGroupLayouts = [] - for itemGroup in itemGroups { + for i in 0 ..< itemGroups.count { + let itemGroup = itemGroups[i] var itemsPerRow = self.itemsPerRow var nativeItemSize = self.nativeItemSize var visibleItemSize = self.visibleItemSize @@ -3129,7 +3194,10 @@ public final class EmojiPagerContentComponent: Component { collapsedItemIndex: collapsedItemIndex, collapsedItemText: collapsedItemText )) - verticalGroupOrigin += groupContentSize.height + groupSpacing + verticalGroupOrigin += groupContentSize.height + if i != itemGroups.count - 1 { + verticalGroupOrigin += groupSpacing + } } verticalGroupOrigin += itemInsets.bottom self.contentSize = CGSize(width: width, height: verticalGroupOrigin) @@ -5901,7 +5969,7 @@ public final class EmojiPagerContentComponent: Component { case let .custom(color): itemLayer.layerTintColor = color.cgColor case .accent: - itemLayer.layerTintColor = keyboardChildEnvironment.theme.list.itemAccentColor.cgColor + itemLayer.layerTintColor = component.customTintColor?.cgColor ?? keyboardChildEnvironment.theme.list.itemAccentColor.cgColor case .primary: itemLayer.layerTintColor = keyboardChildEnvironment.theme.list.itemPrimaryTextColor.cgColor case .none: @@ -5947,7 +6015,7 @@ public final class EmojiPagerContentComponent: Component { itemSelectionLayer.backgroundColor = color.withMultipliedAlpha(0.1).cgColor itemSelectionLayer.tintContainerLayer.backgroundColor = UIColor.clear.cgColor } else if case .accent = item.tintMode { - itemSelectionLayer.backgroundColor = keyboardChildEnvironment.theme.list.itemAccentColor.withMultipliedAlpha(0.1).cgColor + itemSelectionLayer.backgroundColor = component.customTintColor?.withMultipliedAlpha(0.1).cgColor ?? keyboardChildEnvironment.theme.list.itemAccentColor.withMultipliedAlpha(0.1).cgColor itemSelectionLayer.tintContainerLayer.backgroundColor = UIColor.clear.cgColor } else { if useOpaqueTheme { @@ -6799,6 +6867,9 @@ public final class EmojiPagerContentComponent: Component { self.scrollView.contentSize = effectiveContentSize } var scrollIndicatorInsets = pagerEnvironment.containerInsets + if let inputInteraction = component.inputInteractionHolder.inputInteraction, let customLayout = inputInteraction.customLayout, customLayout.topPanelAlwaysHidden { + scrollIndicatorInsets.top += 20.0 + } if self.warpView != nil { scrollIndicatorInsets.bottom += 20.0 } @@ -7183,16 +7254,17 @@ public final class EmojiPagerContentComponent: Component { return hasPremium } - public enum Subject { + public enum Subject: Equatable { case generic case status - case reaction + case reaction(onlyTop: Bool) case emoji case topicIcon case quickReaction case profilePhoto case groupPhoto case backgroundIcon + case reactionList } public static func emojiInputData( @@ -7223,7 +7295,12 @@ public final class EmojiPagerContentComponent: Component { var orderedItemListCollectionIds: [Int32] = [] - orderedItemListCollectionIds.append(Namespaces.OrderedItemList.LocalRecentEmoji) + switch subject { + case .backgroundIcon, .reactionList: + break + default: + orderedItemListCollectionIds.append(Namespaces.OrderedItemList.LocalRecentEmoji) + } var iconStatusEmoji: Signal<[TelegramMediaFile], NoError> = .single([]) @@ -7241,7 +7318,7 @@ public final class EmojiPagerContentComponent: Component { } } |> take(1) - } else if [.reaction, .quickReaction].contains(subject) { + } else if [.reaction(onlyTop: false), .quickReaction].contains(subject) { orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudTopReactions) orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudRecentReactions) } else if case .topicIcon = subject { @@ -7264,14 +7341,14 @@ public final class EmojiPagerContentComponent: Component { } let availableReactions: Signal - if [.reaction, .quickReaction].contains(subject) { + if [.reaction(onlyTop: false), .quickReaction, .reactionList].contains(subject) { availableReactions = context.engine.stickers.availableReactions() } else { availableReactions = .single(nil) } let searchCategories: Signal - if [.emoji, .reaction].contains(subject) { + if [.emoji, .reaction(onlyTop: false), .reactionList].contains(subject) { searchCategories = context.engine.stickers.emojiSearchCategories(kind: .emoji) } else if case .status = subject { searchCategories = context.engine.stickers.emojiSearchCategories(kind: .status) @@ -7656,7 +7733,52 @@ public final class EmojiPagerContentComponent: Component { } } } - } else if [.reaction, .quickReaction].contains(subject) { + } else if subject == .reactionList { + var existingIds = Set() + + if let availableReactions = availableReactions { + for reactionItem in availableReactions.reactions { + if !reactionItem.isEnabled { + continue + } + if existingIds.contains(reactionItem.value) { + continue + } + existingIds.insert(reactionItem.value) + + let icon: EmojiPagerContentComponent.Item.Icon + if !hasPremium, case .custom = reactionItem.value { + icon = .locked + } else { + icon = .none + } + + var tintMode: Item.TintMode = .none + if reactionItem.selectAnimation.isCustomTemplateEmoji { + tintMode = .primary + } + + let animationFile = reactionItem.selectAnimation + let animationData = EntityKeyboardAnimationData(file: animationFile, isReaction: true) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: animationFile, + subgroupId: nil, + icon: icon, + tintMode: tintMode + ) + + let groupId = "liked" + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: nil, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem])) + } + } + } + } else if [.reaction(onlyTop: true), .reaction(onlyTop: false), .quickReaction].contains(subject) { var existingIds = Set() var topReactionItems = topReactionItems @@ -7682,7 +7804,9 @@ public final class EmojiPagerContentComponent: Component { } let maxTopLineCount: Int - if hasPremium { + if case .reaction(onlyTop: true) = subject { + maxTopLineCount = 1000 + } else if hasPremium { maxTopLineCount = 2 } else { maxTopLineCount = 6 @@ -7695,7 +7819,9 @@ public final class EmojiPagerContentComponent: Component { existingIds.insert(reactionItem.reaction) let icon: EmojiPagerContentComponent.Item.Icon - if !hasPremium, case .custom = reactionItem.reaction { + if case .reaction(onlyTop: true) = subject { + icon = .none + } else if !hasPremium, case .custom = reactionItem.reaction { icon = .locked } else { icon = .none @@ -7730,144 +7856,146 @@ public final class EmojiPagerContentComponent: Component { } } - var hasRecent = false - if let recentReactions = recentReactions, !recentReactions.items.isEmpty { - hasRecent = true - } - - let maxRecentLineCount: Int - if hasPremium { - maxRecentLineCount = 10 - } else { - maxRecentLineCount = 10 - } - - let popularTitle = hasRecent ? strings.Chat_ReactionSection_Recent : strings.Chat_ReactionSection_Popular - - if let availableReactions = availableReactions { - for reactionItem in availableReactions.reactions { - if !reactionItem.isEnabled { - continue - } - if existingIds.contains(reactionItem.value) { - continue - } - existingIds.insert(reactionItem.value) - - let icon: EmojiPagerContentComponent.Item.Icon - if !hasPremium, case .custom = reactionItem.value { - icon = .locked - } else { - icon = .none - } - - var tintMode: Item.TintMode = .none - if reactionItem.selectAnimation.isCustomTemplateEmoji { - tintMode = .primary - } - - let animationFile = reactionItem.selectAnimation - let animationData = EntityKeyboardAnimationData(file: animationFile, isReaction: true) - let resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: animationFile, - subgroupId: nil, - icon: icon, - tintMode: tintMode - ) - - if hasPremium { - let groupId = "popular" - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) + if case .reaction(onlyTop: false) = subject { + var hasRecent = false + if let recentReactions = recentReactions, !recentReactions.items.isEmpty { + hasRecent = true + } + + let maxRecentLineCount: Int + if hasPremium { + maxRecentLineCount = 10 + } else { + maxRecentLineCount = 10 + } + + let popularTitle = hasRecent ? strings.Chat_ReactionSection_Recent : strings.Chat_ReactionSection_Popular + + if let availableReactions = availableReactions { + for reactionItem in availableReactions.reactions { + if !reactionItem.isEnabled { + continue + } + if existingIds.contains(reactionItem.value) { + continue + } + existingIds.insert(reactionItem.value) + + let icon: EmojiPagerContentComponent.Item.Icon + if !hasPremium, case .custom = reactionItem.value { + icon = .locked } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: popularTitle, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: hasRecent && subject != .quickReaction, headerItem: nil, items: [resultItem])) + icon = .none } - } else { - let groupId = "recent" - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - - if itemGroups[groupIndex].items.count >= maxRecentLineCount * 8 { - break + + var tintMode: Item.TintMode = .none + if reactionItem.selectAnimation.isCustomTemplateEmoji { + tintMode = .primary + } + + let animationFile = reactionItem.selectAnimation + let animationData = EntityKeyboardAnimationData(file: animationFile, isReaction: true) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: animationFile, + subgroupId: nil, + icon: icon, + tintMode: tintMode + ) + + if hasPremium { + let groupId = "popular" + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: popularTitle, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: hasRecent && subject != .quickReaction, headerItem: nil, items: [resultItem])) } } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: nil, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem])) + let groupId = "recent" + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + + if itemGroups[groupIndex].items.count >= maxRecentLineCount * 8 { + break + } + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: nil, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem])) + } } } } - } - - if let recentReactions = recentReactions { - var popularInsertIndex = 0 - for item in recentReactions.items { - guard let item = item.contents.get(RecentReactionItem.self) else { - continue - } - - let animationFile: TelegramMediaFile - let icon: EmojiPagerContentComponent.Item.Icon - - switch item.content { - case let .builtin(value): - if existingIds.contains(.builtin(value)) { + + if let recentReactions = recentReactions { + var popularInsertIndex = 0 + for item in recentReactions.items { + guard let item = item.contents.get(RecentReactionItem.self) else { continue } - existingIds.insert(.builtin(value)) - if let availableReactions = availableReactions, let availableReaction = availableReactions.reactions.first(where: { $0.value == .builtin(value) }) { - if let centerAnimation = availableReaction.centerAnimation { - animationFile = centerAnimation + + let animationFile: TelegramMediaFile + let icon: EmojiPagerContentComponent.Item.Icon + + switch item.content { + case let .builtin(value): + if existingIds.contains(.builtin(value)) { + continue + } + existingIds.insert(.builtin(value)) + if let availableReactions = availableReactions, let availableReaction = availableReactions.reactions.first(where: { $0.value == .builtin(value) }) { + if let centerAnimation = availableReaction.centerAnimation { + animationFile = centerAnimation + } else { + continue + } } else { continue } - } else { - continue + + icon = .none + case let .custom(file): + if existingIds.contains(.custom(file.fileId.id)) { + continue + } + existingIds.insert(.custom(file.fileId.id)) + animationFile = file + + if !hasPremium { + icon = .locked + } else { + icon = .none + } } - icon = .none - case let .custom(file): - if existingIds.contains(.custom(file.fileId.id)) { - continue + var tintMode: Item.TintMode = .none + if animationFile.isCustomTemplateEmoji { + tintMode = .primary } - existingIds.insert(.custom(file.fileId.id)) - animationFile = file - if !hasPremium { - icon = .locked + let animationData = EntityKeyboardAnimationData(file: animationFile, isReaction: true) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: animationFile, + subgroupId: nil, + icon: icon, + tintMode: tintMode + ) + + let groupId = "popular" + if let groupIndex = itemGroupIndexById[groupId] { + if itemGroups[groupIndex].items.count + 1 >= maxRecentLineCount * 8 { + break + } + + itemGroups[groupIndex].items.insert(resultItem, at: popularInsertIndex) + popularInsertIndex += 1 } else { - icon = .none - } - } - - var tintMode: Item.TintMode = .none - if animationFile.isCustomTemplateEmoji { - tintMode = .primary - } - - let animationData = EntityKeyboardAnimationData(file: animationFile, isReaction: true) - let resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: animationFile, - subgroupId: nil, - icon: icon, - tintMode: tintMode - ) - - let groupId = "popular" - if let groupIndex = itemGroupIndexById[groupId] { - if itemGroups[groupIndex].items.count + 1 >= maxRecentLineCount * 8 { - break + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: popularTitle, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: hasRecent && subject != .quickReaction, headerItem: nil, items: [resultItem])) } - - itemGroups[groupIndex].items.insert(resultItem, at: popularInsertIndex) - popularInsertIndex += 1 - } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: popularTitle, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: hasRecent && subject != .quickReaction, headerItem: nil, items: [resultItem])) } } } @@ -7928,7 +8056,7 @@ public final class EmojiPagerContentComponent: Component { } } } - } else if case .backgroundIcon = subject { + } else if case .backgroundIcon = subject { var existingIds = Set() let resultItem = EmojiPagerContentComponent.Item( @@ -7937,7 +8065,7 @@ public final class EmojiPagerContentComponent: Component { itemFile: nil, subgroupId: nil, icon: .none, - tintMode: backgroundIconColor.flatMap { .custom($0) } ?? .accent + tintMode: .accent ) let groupId = "recent" @@ -8004,7 +8132,7 @@ public final class EmojiPagerContentComponent: Component { } } - let hasRecentEmoji = ![.reaction, .quickReaction, .status, .profilePhoto, .groupPhoto, .topicIcon, .backgroundIcon].contains(subject) + let hasRecentEmoji = ![.reaction(onlyTop: true), .reaction(onlyTop: false), .quickReaction, .status, .profilePhoto, .groupPhoto, .topicIcon, .backgroundIcon, .reactionList].contains(subject) if let recentEmoji = recentEmoji, hasRecentEmoji { for item in recentEmoji.items { @@ -8070,7 +8198,7 @@ public final class EmojiPagerContentComponent: Component { } var icon: EmojiPagerContentComponent.Item.Icon = .none - if [.reaction, .quickReaction].contains(subject), !hasPremium { + if [.reaction(onlyTop: false), .quickReaction].contains(subject), !hasPremium { icon = .locked } @@ -8228,7 +8356,12 @@ public final class EmojiPagerContentComponent: Component { ) } - itemGroups.append(ItemGroup(supergroupId: supergroupId, id: groupId, title: featuredEmojiPack.info.title, subtitle: nil, isPremiumLocked: isPremiumLocked, isFeatured: true, collapsedLineCount: 3, isClearable: false, headerItem: headerItem, items: [resultItem])) + var isFeatured = true + if case .reactionList = subject { + isFeatured = false + } + + itemGroups.append(ItemGroup(supergroupId: supergroupId, id: groupId, title: featuredEmojiPack.info.title, subtitle: nil, isPremiumLocked: isPremiumLocked, isFeatured: isFeatured, collapsedLineCount: 3, isClearable: false, headerItem: headerItem, items: [resultItem])) } } } @@ -8242,7 +8375,7 @@ public final class EmojiPagerContentComponent: Component { var displaySearchWithPlaceholder: String? let searchInitiallyHidden = true if hasSearch { - if [.reaction, .quickReaction].contains(subject) { + if [.reaction(onlyTop: false), .quickReaction].contains(subject) { displaySearchWithPlaceholder = strings.EmojiSearch_SearchReactionsPlaceholder } else if case .status = subject { displaySearchWithPlaceholder = strings.EmojiSearch_SearchStatusesPlaceholder @@ -8297,8 +8430,8 @@ public final class EmojiPagerContentComponent: Component { ) } - let warpContentsOnEdges = [.reaction, .quickReaction, .status, .profilePhoto, .groupPhoto, .backgroundIcon].contains(subject) - let enableLongPress = [.reaction, .status].contains(subject) + let warpContentsOnEdges = [.reaction(onlyTop: true), .reaction(onlyTop: false), .quickReaction, .status, .profilePhoto, .groupPhoto, .backgroundIcon].contains(subject) + let enableLongPress = [.reaction(onlyTop: true), .reaction(onlyTop: false), .status].contains(subject) return EmojiPagerContentComponent( id: "emoji", @@ -8321,7 +8454,8 @@ public final class EmojiPagerContentComponent: Component { searchIsPlaceholderOnly: false, emptySearchResults: nil, enableLongPress: enableLongPress, - selectedItems: selectedItems + selectedItems: selectedItems, + customTintColor: backgroundIconColor ) } return emojiItems @@ -8802,7 +8936,8 @@ public final class EmojiPagerContentComponent: Component { searchIsPlaceholderOnly: searchIsPlaceholderOnly, emptySearchResults: nil, enableLongPress: false, - selectedItems: Set() + selectedItems: Set(), + customTintColor: nil ) } } diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchContent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchContent.swift index 9763f7dba30..cc21a8caa10 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchContent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchContent.swift @@ -468,7 +468,8 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode searchIsPlaceholderOnly: false, emptySearchResults: nil, enableLongPress: false, - selectedItems: Set() + selectedItems: Set(), + customTintColor: nil ) if let emojiSearchResult = self.immediateEmojiSearchState.result { diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchStatusComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchStatusComponent.swift index 8d35cb2bf35..4b498d229b0 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchStatusComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchStatusComponent.swift @@ -771,12 +771,12 @@ final class EmojiSearchStatusComponent: Component { if needsAnimation { if self.displayLink == nil { var counter = 0 - self.displayLink = SharedDisplayLinkDriver.shared.add(needsHighestFramerate: false, { [weak self] in + self.displayLink = SharedDisplayLinkDriver.shared.add { [weak self] _ in counter += 1 if counter % 1 == 0 { self?.updateAnimation() } - }) + } } } else { if let displayLink = self.displayLink { diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift index 7ca5c7da763..b88c847bed3 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift @@ -349,12 +349,13 @@ public final class EntityKeyboardComponent: Component { let iconMapping: [String: EntityKeyboardIconTopPanelComponent.Icon] = [ "saved": .saved, "recent": .recent, - "premium": .premium + "premium": .premium, + "liked": .liked ] let titleMapping: [String: String] = [ "saved": component.strings.Stickers_Favorites, "recent": component.strings.Stickers_Recent, - "premium": component.strings.EmojiInput_PanelTitlePremium + "premium": component.strings.EmojiInput_PanelTitlePremium, ] if let icon = iconMapping[id], let title = titleMapping[id] { topMaskItems.append(EntityKeyboardTopPanelComponent.Item( @@ -387,6 +388,7 @@ public final class EntityKeyboardComponent: Component { animationRenderer: maskContent.animationRenderer, theme: component.theme, title: itemGroup.title ?? "", + customTintColor: component.customTintColor, pressed: { [weak self] in self?.scrollToItemGroup(contentId: "masks", groupId: itemGroup.supergroupId, subgroupId: nil) } @@ -486,6 +488,7 @@ public final class EntityKeyboardComponent: Component { let iconMapping: [String: EntityKeyboardIconTopPanelComponent.Icon] = [ "saved": .saved, "recent": .recent, + "liked": .liked, "premium": .premium ] let titleMapping: [String: String] = [ @@ -525,6 +528,7 @@ public final class EntityKeyboardComponent: Component { animationRenderer: stickerContent.animationRenderer, theme: component.theme, title: itemGroup.title ?? "", + customTintColor: component.customTintColor, pressed: { [weak self] in self?.scrollToItemGroup(contentId: "stickers", groupId: itemGroup.supergroupId, subgroupId: nil) } @@ -586,12 +590,14 @@ public final class EntityKeyboardComponent: Component { for itemGroup in emojiContent.panelItemGroups { if !itemGroup.items.isEmpty { if let id = itemGroup.groupId.base as? String { - if id == "recent" { + if id == "recent" || id == "liked" { let iconMapping: [String: EntityKeyboardIconTopPanelComponent.Icon] = [ "recent": .recent, + "liked": .liked, ] let titleMapping: [String: String] = [ "recent": component.strings.Stickers_Recent, + "liked": "", ] if let icon = iconMapping[id], let title = titleMapping[id] { topEmojiItems.append(EntityKeyboardTopPanelComponent.Item( @@ -639,7 +645,7 @@ public final class EntityKeyboardComponent: Component { animationRenderer: emojiContent.animationRenderer, theme: component.theme, title: itemGroup.title ?? "", - customTintColor: itemGroup.customTintColor, + customTintColor: component.customTintColor ?? itemGroup.customTintColor, pressed: { [weak self] in self?.scrollToItemGroup(contentId: "emoji", groupId: itemGroup.supergroupId, subgroupId: nil) } diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift index 8f2186a215e..eea256000d4 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift @@ -169,9 +169,9 @@ final class EntityKeyboardAnimationTopPanelComponent: Component { case .none: break case .primary: - itemLayer.layerTintColor = component.theme.list.itemPrimaryTextColor.cgColor + itemLayer.layerTintColor = component.customTintColor?.cgColor ?? component.theme.list.itemPrimaryTextColor.cgColor case .accent: - itemLayer.layerTintColor = component.theme.list.itemAccentColor.cgColor + itemLayer.layerTintColor = component.customTintColor?.cgColor ?? component.theme.list.itemAccentColor.cgColor case let .custom(color): itemLayer.layerTintColor = component.customTintColor?.cgColor ?? color.cgColor } @@ -276,6 +276,7 @@ final class EntityKeyboardIconTopPanelComponent: Component { case recent case saved case premium + case liked } let icon: Icon @@ -360,6 +361,8 @@ final class EntityKeyboardIconTopPanelComponent: Component { image = UIImage(bundleImageName: "Chat/Input/Media/PanelRecentIcon") case .saved: image = UIImage(bundleImageName: "Chat/Input/Media/PanelSavedIcon") + case .liked: + image = UIImage(bundleImageName: "Chat/Input/Media/PanelHeartIcon")?.withRenderingMode(.alwaysTemplate) case .premium: image = generateImage(CGSize(width: 44.0, height: 44.0), contextGenerator: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) @@ -2078,7 +2081,7 @@ public final class EntityKeyboardTopPanelComponent: Component { } let isRound: Bool - if let string = activeContentItemId.base as? String, (string == "featuredTop" || string == "recent" || string == "static" || string == "trending") { + if let string = activeContentItemId.base as? String, (string == "featuredTop" || string == "recent" || string == "static" || string == "trending" || string == "liked") { isRound = true } else { isRound = false diff --git a/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift b/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift index 3ea69b63a50..1cd12e7d6a7 100644 --- a/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift +++ b/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift @@ -249,6 +249,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { audioRecorder: nil, videoRecordingStatus: nil, isRecordingLocked: false, + hasRecordedVideo: false, recordedAudioPreview: nil, hasRecordedVideoPreview: false, wasRecordingDismissed: false, @@ -261,6 +262,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { customInputView: self.inputView, forceIsEditing: false, disabledPlaceholder: nil, + header: nil, isChannel: false, storyItem: nil, chatLocation: self.chatLocation diff --git a/submodules/TelegramUI/Components/LottieComponent/Sources/LottieComponent.swift b/submodules/TelegramUI/Components/LottieComponent/Sources/LottieComponent.swift index b7111f6d384..b1c016e6327 100644 --- a/submodules/TelegramUI/Components/LottieComponent/Sources/LottieComponent.swift +++ b/submodules/TelegramUI/Components/LottieComponent/Sources/LottieComponent.swift @@ -263,23 +263,23 @@ public final class LottieComponent: Component { self.currentFrameStartTime = CACurrentMediaTime() if self.displayLink == nil { - self.displayLink = SharedDisplayLinkDriver.shared.add(needsHighestFramerate: false, { [weak self] in + self.displayLink = SharedDisplayLinkDriver.shared.add { [weak self] _ in guard let self else { return } self.advanceIfNeeded() - }) + } } }) } else { self.currentFrameStartTime = CACurrentMediaTime() if self.displayLink == nil { - self.displayLink = SharedDisplayLinkDriver.shared.add(needsHighestFramerate: false, { [weak self] in + self.displayLink = SharedDisplayLinkDriver.shared.add { [weak self] _ in guard let self else { return } self.advanceIfNeeded() - }) + } } } } diff --git a/submodules/TelegramUI/Components/MediaEditor/MetalResources/EditorAdjustments.metal b/submodules/TelegramUI/Components/MediaEditor/MetalResources/EditorAdjustments.metal index 31f4f02380f..730de12915d 100644 --- a/submodules/TelegramUI/Components/MediaEditor/MetalResources/EditorAdjustments.metal +++ b/submodules/TelegramUI/Components/MediaEditor/MetalResources/EditorAdjustments.metal @@ -88,10 +88,10 @@ half3 applyRGBCurve(half3 pixel, constant float redCurve[200], constant float gr fragment half4 adjustmentsFragmentShader(RasterizerData in [[stage_in]], texture2d sourceImage [[texture(0)]], constant MediaEditorAdjustments& adjustments [[buffer(0)]], - constant float allCurve [[buffer(1)]][200], - constant float redCurve [[buffer(2)]][200], - constant float greenCurve [[buffer(3)]][200], - constant float blueCurve [[buffer(4)]][200] + constant float allCurve [[buffer(1)]][200], + constant float redCurve [[buffer(2)]][200], + constant float greenCurve [[buffer(3)]][200], + constant float blueCurve [[buffer(4)]][200] ) { constexpr sampler samplr(filter::linear, mag_filter::linear, min_filter::linear); const float epsilon = 0.005; diff --git a/submodules/TelegramUI/Components/MediaEditor/MetalResources/EditorDefault.metal b/submodules/TelegramUI/Components/MediaEditor/MetalResources/EditorDefault.metal index fec47fb8107..d03c2f7e8f5 100644 --- a/submodules/TelegramUI/Components/MediaEditor/MetalResources/EditorDefault.metal +++ b/submodules/TelegramUI/Components/MediaEditor/MetalResources/EditorDefault.metal @@ -37,3 +37,14 @@ fragment half histogramPrepareFragmentShader(RasterizerData in [[stage_in]], half luma = color.r * 0.3 + color.g * 0.59 + color.b * 0.11; return luma; } + +typedef struct { + float3 topColor; + float3 bottomColor; +} GradientColors; + +fragment half4 gradientFragmentShader(RasterizerData in [[stage_in]], + constant GradientColors& colors [[buffer(0)]]) { + + return half4(half3(mix(colors.topColor, colors.bottomColor, in.texCoord.y)), 1.0); +} diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingStickerEntity.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingStickerEntity.swift index d18276e8d05..41e3fda58ae 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingStickerEntity.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingStickerEntity.swift @@ -30,7 +30,7 @@ public final class DrawingStickerEntity: DrawingEntity, Codable { case file(TelegramMediaFile, FileType) case image(UIImage, ImageType) case video(TelegramMediaFile) - case dualVideoReference + case dualVideoReference(Bool) public static func == (lhs: Content, rhs: Content) -> Bool { switch lhs { @@ -52,8 +52,8 @@ public final class DrawingStickerEntity: DrawingEntity, Codable { } else { return false } - case .dualVideoReference: - if case .dualVideoReference = rhs { + case let .dualVideoReference(isAdditional): + if case .dualVideoReference(isAdditional) = rhs { return true } else { return false @@ -71,6 +71,7 @@ public final class DrawingStickerEntity: DrawingEntity, Codable { case isRectangle case isDualPhoto case dualVideo + case isAdditionalVideo case referenceDrawingSize case position case scale @@ -184,7 +185,8 @@ public final class DrawingStickerEntity: DrawingEntity, Codable { let container = try decoder.container(keyedBy: CodingKeys.self) self.uuid = try container.decode(UUID.self, forKey: .uuid) if let _ = try container.decodeIfPresent(Bool.self, forKey: .dualVideo) { - self.content = .dualVideoReference + let isAdditional = try container.decodeIfPresent(Bool.self, forKey: .isAdditionalVideo) ?? false + self.content = .dualVideoReference(isAdditional) } else if let file = try container.decodeIfPresent(TelegramMediaFile.self, forKey: .file) { let fileType: Content.FileType if let reaction = try container.decodeIfPresent(MessageReaction.Reaction.self, forKey: .reaction) { @@ -257,8 +259,9 @@ public final class DrawingStickerEntity: DrawingEntity, Codable { } case let .video(file): try container.encode(file, forKey: .videoFile) - case .dualVideoReference: + case let .dualVideoReference(isAdditional): try container.encode(true, forKey: .dualVideo) + try container.encode(isAdditional, forKey: .isAdditionalVideo) } try container.encode(self.referenceDrawingSize, forKey: .referenceDrawingSize) try container.encode(self.position, forKey: .position) diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/ImageTextureSource.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/ImageTextureSource.swift deleted file mode 100644 index 75ae7912bfc..00000000000 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/ImageTextureSource.swift +++ /dev/null @@ -1,96 +0,0 @@ -import Foundation -import AVFoundation -import Metal -import MetalKit -import Display -import Accelerate - -func loadTexture(image: UIImage, device: MTLDevice) -> MTLTexture? { - func dataForImage(_ image: UIImage) -> UnsafeMutablePointer { - let imageRef = image.cgImage - let width = Int(image.size.width) - let height = Int(image.size.height) - let colorSpace = CGColorSpaceCreateDeviceRGB() - - let rawData = UnsafeMutablePointer.allocate(capacity: width * height * 4) - let bytePerPixel = 4 - let bytesPerRow = bytePerPixel * Int(width) - let bitsPerComponent = 8 - let bitmapInfo = CGBitmapInfo.byteOrder32Little.rawValue + CGImageAlphaInfo.premultipliedFirst.rawValue - let context = CGContext.init(data: rawData, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) - context?.draw(imageRef!, in: CGRect(x: 0, y: 0, width: width, height: height)) - - return rawData - } - - let width = Int(image.size.width * image.scale) - let height = Int(image.size.height * image.scale) - let bytePerPixel = 4 - let bytesPerRow = bytePerPixel * width - - var texture : MTLTexture? - let region = MTLRegionMake2D(0, 0, Int(width), Int(height)) - let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .bgra8Unorm, width: width, height: height, mipmapped: false) - texture = device.makeTexture(descriptor: textureDescriptor) - - let data = dataForImage(image) - texture?.replace(region: region, mipmapLevel: 0, withBytes: data, bytesPerRow: bytesPerRow) - - return texture -} - -final class ImageTextureSource: TextureSource { - weak var output: TextureConsumer? - - var texture: MTLTexture? - - init(image: UIImage, renderTarget: RenderTarget) { - if let device = renderTarget.mtlDevice { - self.texture = loadTexture(image: image, device: device) - } - } - - func connect(to consumer: TextureConsumer) { - self.output = consumer - if let texture = self.texture { - self.output?.consumeTexture(texture, render: false) - } - } - - func invalidate() { - self.texture = nil - } -} - -func pixelBufferToMTLTexture(pixelBuffer: CVPixelBuffer, textureCache: CVMetalTextureCache) -> MTLTexture? { - let width = CVPixelBufferGetWidth(pixelBuffer) - let height = CVPixelBufferGetHeight(pixelBuffer) - - let format: MTLPixelFormat = .r8Unorm - var textureRef : CVMetalTexture? - let status = CVMetalTextureCacheCreateTextureFromImage(nil, textureCache, pixelBuffer, nil, format, width, height, 0, &textureRef) - if status == kCVReturnSuccess { - return CVMetalTextureGetTexture(textureRef!) - } - - return nil -} - -func getTextureImage(device: MTLDevice, texture: MTLTexture, mirror: Bool = false) -> UIImage? { - let colorSpace = CGColorSpaceCreateDeviceRGB() - let context = CIContext(mtlDevice: device, options: [:]) - guard var ciImage = CIImage(mtlTexture: texture, options: [.colorSpace: colorSpace]) else { - return nil - } - let transform: CGAffineTransform - if mirror { - transform = CGAffineTransform(-1.0, 0.0, 0.0, -1.0, ciImage.extent.width, ciImage.extent.height) - } else { - transform = CGAffineTransform(1.0, 0.0, 0.0, -1.0, 0.0, ciImage.extent.height) - } - ciImage = ciImage.transformed(by: transform) - guard let cgImage = context.createCGImage(ciImage, from: CGRect(origin: .zero, size: CGSize(width: ciImage.extent.width, height: ciImage.extent.height))) else { - return nil - } - return UIImage(cgImage: cgImage) -} diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift index 18146fd1f50..3baa09442c0 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift @@ -12,19 +12,77 @@ import FastBlur import AccountContext public struct MediaEditorPlayerState { + public struct Track: Equatable { + public enum Content: Equatable { + case video(frames: [UIImage], framesUpdateTimestamp: Double) + case audio(artist: String?, title: String?, samples: Data?, peak: Int32) + + public static func ==(lhs: Content, rhs: Content) -> Bool { + switch lhs { + case let .video(_, framesUpdateTimestamp): + if case .video(_, framesUpdateTimestamp) = rhs { + return true + } else { + return false + } + case let .audio(lhsArtist, lhsTitle, lhsSamples, lhsPeak): + if case let .audio(rhsArtist, rhsTitle, rhsSamples, rhsPeak) = rhs { + return lhsArtist == rhsArtist && lhsTitle == rhsTitle && lhsSamples == rhsSamples && lhsPeak == rhsPeak + } else { + return false + } + } + } + } + + public let id: Int32 + public let content: Content + public let duration: Double + public let trimRange: Range? + public let offset: Double? + public let isMain: Bool + public let visibleInTimeline: Bool + } + public let generationTimestamp: Double - public let duration: Double - public let timeRange: Range? + public let tracks: [Track] public let position: Double public let isPlaying: Bool - public let frames: [UIImage] - public let framesCount: Int - public let framesUpdateTimestamp: Double - public let hasAudio: Bool - public let isAudioPlayerOnly: Bool + + public var isAudioOnly: Bool { + var hasVideoTrack = false + var hasAudioTrack = false + for track in tracks { + switch track.content { + case .video: + hasVideoTrack = true + case .audio: + hasAudioTrack = true + } + } + return !hasVideoTrack && hasAudioTrack + } + + public var hasAudio: Bool { + return true + } } public final class MediaEditor { + public struct GradientColors { + public let top: UIColor + public let bottom: UIColor + + public init(top: UIColor, bottom: UIColor) { + self.top = top + self.bottom = bottom + } + + public var array: [UIColor] { + return [self.top, self.bottom] + } + } + public enum Subject { case image(UIImage, PixelDimensions) case video(String, UIImage?, Bool, String?, PixelDimensions, Double) @@ -47,13 +105,20 @@ public final class MediaEditor { private let subject: Subject private let clock = CMClockGetHostTimeClock() + private var player: AVPlayer? + private var playerAudioMix: AVMutableAudioMix? + private var additionalPlayer: AVPlayer? + private var additionalPlayerAudioMix: AVMutableAudioMix? + private var audioPlayer: AVPlayer? + private var audioPlayerAudioMix: AVMutableAudioMix? + + private var volumeFadeIn: SwiftSignalKit.Timer? private var timeObserver: Any? private weak var timeObserverPlayer: AVPlayer? - private var didPlayToEndTimeObserver: NSObjectProtocol? private weak var previewView: MediaEditorPreviewView? @@ -76,15 +141,15 @@ public final class MediaEditor { private var textureSourceDisposable: Disposable? - private let gradientColorsPromise = Promise<(UIColor, UIColor)?>() - public var gradientColors: Signal<(UIColor, UIColor)?, NoError> { - return self.gradientColorsPromise.get() - } - private var gradientColorsValue: (UIColor, UIColor)? { + private let gradientColorsPromise = Promise() + private var gradientColorsValue: GradientColors? { didSet { self.gradientColorsPromise.set(.single(self.gradientColorsValue)) } } + public var gradientColors: Signal { + return self.gradientColorsPromise.get() + } private let histogramPromise = Promise() public var histogram: Signal { @@ -115,7 +180,7 @@ public final class MediaEditor { } public var resultIsVideo: Bool { - return self.player != nil || self.audioPlayer != nil || self.values.entities.contains(where: { $0.entity.isAnimated }) + return self.player != nil || self.audioPlayer != nil || self.additionalPlayer != nil || self.values.entities.contains(where: { $0.entity.isAnimated }) } public var resultImage: UIImage? { @@ -127,17 +192,41 @@ public final class MediaEditor { } private let playerPromise = Promise() - private var playerPlaybackState: (Double, Double, Bool, Bool, Bool) = (0.0, 0.0, false, false, false) { + private let additionalPlayerPromise = Promise(nil) + private let audioPlayerPromise = Promise(nil) + + private struct PlaybackState: Equatable { + let duration: Double + let position: Double + let isPlaying: Bool + let hasAudio: Bool + + init() { + self.duration = 0.0 + self.position = 0.0 + self.isPlaying = false + self.hasAudio = false + } + + init(duration: Double, position: Double, isPlaying: Bool, hasAudio: Bool) { + self.duration = duration + self.position = position + self.isPlaying = isPlaying + self.hasAudio = hasAudio + } + } + + private var playerPlaybackState: PlaybackState = PlaybackState() { didSet { self.playerPlaybackStatePromise.set(.single(self.playerPlaybackState)) } } - private let playerPlaybackStatePromise = Promise<(Double, Double, Bool, Bool, Bool)>((0.0, 0.0, false, false, false)) + private let playerPlaybackStatePromise = Promise(PlaybackState()) public var position: Signal { return self.playerPlaybackStatePromise.get() - |> map { _, position, _, _, _ -> Double in - return position + |> map { state -> Double in + return state.position } } @@ -146,16 +235,32 @@ public final class MediaEditor { if let trimRange = self.values.videoTrimRange { return trimRange.upperBound - trimRange.lowerBound } else { - return min(60.0, self.playerPlaybackState.0) + return min(60.0, self.playerPlaybackState.duration) } } else { return nil } } + public var mainVideoDuration: Double? { + if self.player != nil { + return min(60.0, self.playerPlaybackState.duration) + } else { + return nil + } + } + + public var additionalVideoDuration: Double? { + if let additionalPlayer = self.additionalPlayer { + return min(60.0, additionalPlayer.currentItem?.asset.duration.seconds ?? 0.0) + } else { + return nil + } + } + public var originalDuration: Double? { - if let _ = self.player { - return min(60.0, self.playerPlaybackState.0) + if self.player != nil || self.additionalPlayer != nil { + return min(60.0, self.playerPlaybackState.duration) } else { return nil } @@ -164,132 +269,110 @@ public final class MediaEditor { public var onFirstDisplay: () -> Void = {} public func playerState(framesCount: Int) -> Signal { - return self.playerPromise.get() - |> mapToSignal { [weak self] player in - if let self, player != nil { - if player === self.player, let asset = player?.currentItem?.asset { - return combineLatest(self.valuesPromise.get(), self.playerPlaybackStatePromise.get(), self.videoFrames(asset: asset, count: framesCount)) - |> map { values, durationAndPosition, framesAndUpdateTimestamp in - let (duration, position, isPlaying, hasAudio, isAudioPlayerOnly) = durationAndPosition - let (frames, framesUpdateTimestamp) = framesAndUpdateTimestamp - return MediaEditorPlayerState( - generationTimestamp: CACurrentMediaTime(), - duration: duration, - timeRange: values.videoTrimRange, - position: position, - isPlaying: isPlaying, - frames: frames, - framesCount: framesCount, - framesUpdateTimestamp: framesUpdateTimestamp, - hasAudio: hasAudio, - isAudioPlayerOnly: isAudioPlayerOnly - ) - } - } else if player === self.audioPlayer { - return combineLatest(self.valuesPromise.get(), self.playerPlaybackStatePromise.get()) - |> map { values, durationAndPosition in - let (duration, position, isPlaying, _, _) = durationAndPosition - return MediaEditorPlayerState( - generationTimestamp: CACurrentMediaTime(), - duration: duration, - timeRange: values.audioTrackTrimRange, - position: position, - isPlaying: isPlaying, - frames: [], - framesCount: 0, - framesUpdateTimestamp: 0, - hasAudio: false, - isAudioPlayerOnly: true - ) - } + func artistAndTitleForTrack(_ audioTrack: MediaAudioTrack) -> (artist: String?, title: String?) { + let artist = audioTrack.artist + var title = audioTrack.title + if artist == nil && title == nil { + if let underscoreIndex = audioTrack.path.firstIndex(of: "_"), let dotIndex = audioTrack.path.lastIndex(of: ".") { + title = String(audioTrack.path[audioTrack.path.index(after: underscoreIndex).. Signal<([UIImage], Double), NoError> { - func blurredImage(_ image: UIImage) -> UIImage? { - guard let image = image.cgImage else { - return nil - } - - let thumbnailSize = CGSize(width: image.width, height: image.height) - let thumbnailContextSize = thumbnailSize.aspectFilled(CGSize(width: 20.0, height: 20.0)) - if let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0) { - thumbnailContext.withFlippedContext { c in - c.interpolationQuality = .none - c.draw(image, in: CGRect(origin: CGPoint(), size: thumbnailContextSize)) - } - imageFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) - - let thumbnailContext2Size = thumbnailSize.aspectFitted(CGSize(width: 100.0, height: 100.0)) - if let thumbnailContext2 = DrawingContext(size: thumbnailContext2Size, scale: 1.0) { - thumbnailContext2.withFlippedContext { c in - c.interpolationQuality = .none - if let image = thumbnailContext.generateImage()?.cgImage { - c.draw(image, in: CGRect(origin: CGPoint(), size: thumbnailContext2Size)) - } + + func playerAndThumbnails(promise: Promise, mirror: Bool = false) -> Signal<(AVPlayer, [UIImage], Double)?, NoError> { + return promise.get() + |> mapToSignal { player -> Signal<(AVPlayer, [UIImage], Double)?, NoError> in + if let player, let asset = player.currentItem?.asset { + return videoFrames(asset: asset, count: framesCount, mirror: mirror) + |> map { framesAndUpdateTimestamp in + return (player, framesAndUpdateTimestamp.0, framesAndUpdateTimestamp.1) } - imageFastBlur(Int32(thumbnailContext2Size.width), Int32(thumbnailContext2Size.height), Int32(thumbnailContext2.bytesPerRow), thumbnailContext2.bytes) - return thumbnailContext2.generateImage() + } else { + return .single(nil) } } - return nil } - guard count > 0 else { - return .complete() - } - let scale = UIScreen.main.scale - let imageGenerator = AVAssetImageGenerator(asset: asset) - imageGenerator.maximumSize = CGSize(width: 48.0 * scale, height: 36.0 * scale) - imageGenerator.appliesPreferredTrackTransform = true - imageGenerator.requestedTimeToleranceBefore = .zero - imageGenerator.requestedTimeToleranceAfter = .zero - - var firstFrame: UIImage - if let cgImage = try? imageGenerator.copyCGImage(at: .zero, actualTime: nil) { - firstFrame = UIImage(cgImage: cgImage) - if let blurred = blurredImage(firstFrame) { - firstFrame = blurred - } - } else { - firstFrame = generateSingleColorImage(size: CGSize(width: 24.0, height: 36.0), color: .black)! - } - return Signal { subscriber in - subscriber.putNext((Array(repeating: firstFrame, count: count), CACurrentMediaTime())) + return combineLatest( + playerAndThumbnails(promise: self.playerPromise), + playerAndThumbnails(promise: self.additionalPlayerPromise, mirror: true), + self.audioPlayerPromise.get(), + self.valuesPromise.get(), + self.playerPlaybackStatePromise.get() + ) |> map { mainPlayerAndThumbnails, additionalPlayerAndThumbnails, audioPlayer, values, playbackState in + var tracks: [MediaEditorPlayerState.Track] = [] - var timestamps: [NSValue] = [] - let duration = asset.duration.seconds - let interval = duration / Double(count) - for i in 0 ..< count { - timestamps.append(NSValue(time: CMTime(seconds: Double(i) * interval, preferredTimescale: CMTimeScale(1000)))) + if let (player, frames, updateTimestamp) = mainPlayerAndThumbnails { + let duration: Double + if !playbackState.duration.isNaN { + duration = playbackState.duration + } else { + duration = player.currentItem?.asset.duration.seconds ?? 0.0 + } + tracks.append(MediaEditorPlayerState.Track( + id: 0, + content: .video( + frames: frames, + framesUpdateTimestamp: updateTimestamp + ), + duration: duration, + trimRange: values.videoTrimRange, + offset: nil, + isMain: tracks.isEmpty, + visibleInTimeline: true + )) } - - var updatedFrames: [UIImage] = [] - imageGenerator.generateCGImagesAsynchronously(forTimes: timestamps) { _, image, _, _, _ in - if let image { - updatedFrames.append(UIImage(cgImage: image)) - if updatedFrames.count == count { - subscriber.putNext((updatedFrames, CACurrentMediaTime())) - subscriber.putCompletion() - } else { - var tempFrames = updatedFrames - for _ in 0 ..< count - updatedFrames.count { - tempFrames.append(firstFrame) - } - subscriber.putNext((tempFrames, CACurrentMediaTime())) - } + if let (player, frames, updateTimestamp) = additionalPlayerAndThumbnails { + let duration: Double + if !playbackState.duration.isNaN && mainPlayerAndThumbnails == nil { + duration = playbackState.duration + } else { + duration = player.currentItem?.asset.duration.seconds ?? 0.0 } + tracks.append(MediaEditorPlayerState.Track( + id: 1, + content: .video( + frames: frames, + framesUpdateTimestamp: updateTimestamp + ), + duration: duration, + trimRange: values.additionalVideoTrimRange, + offset: values.additionalVideoOffset, + isMain: tracks.isEmpty, + visibleInTimeline: !values.additionalVideoIsDual + )) + } + if let audioTrack = values.audioTrack { + let (artist, title) = artistAndTitleForTrack(audioTrack) + tracks.append(MediaEditorPlayerState.Track( + id: 2, + content: .audio( + artist: artist, + title: title, + samples: values.audioTrackSamples?.samples, + peak: values.audioTrackSamples?.peak ?? 0 + ), + duration: audioTrack.duration, + trimRange: values.audioTrackTrimRange, + offset: values.audioTrackOffset, + isMain: tracks.isEmpty, + visibleInTimeline: true + )) } - return ActionDisposable { - imageGenerator.cancelAllCGImageGeneration() + guard !tracks.isEmpty else { + return nil } + + return MediaEditorPlayerState( + generationTimestamp: CACurrentMediaTime(), + tracks: tracks, + position: playbackState.position, + isPlaying: playbackState.isPlaying + ) } } @@ -314,11 +397,16 @@ public final class MediaEditor { videoIsMuted: false, videoIsFullHd: false, videoIsMirrored: false, + videoVolume: 1.0, additionalVideoPath: nil, + additionalVideoIsDual: false, additionalVideoPosition: nil, additionalVideoScale: nil, additionalVideoRotation: nil, additionalVideoPositionChanges: [], + additionalVideoTrimRange: nil, + additionalVideoOffset: nil, + additionalVideoVolume: nil, drawing: nil, entities: [], toolValues: [:], @@ -344,44 +432,30 @@ public final class MediaEditor { } if case let .asset(asset) = subject { - self.playerPlaybackState = (asset.duration, 0.0, false, false, false) + self.playerPlaybackState = PlaybackState(duration: asset.duration, position: 0.0, isPlaying: false, hasAudio: asset.mediaType == .video) self.playerPlaybackStatePromise.set(.single(self.playerPlaybackState)) } else if case let .video(_, _, _, _, _, duration) = subject { - self.playerPlaybackState = (duration, 0.0, false, true, false) + self.playerPlaybackState = PlaybackState(duration: duration, position: 0.0, isPlaying: false, hasAudio: true) self.playerPlaybackStatePromise.set(.single(self.playerPlaybackState)) } } deinit { self.textureSourceDisposable?.dispose() - self.destroyTimeObservers() + self.invalidateTimeObservers() } - private func destroyTimeObservers() { - if let timeObserver = self.timeObserver { - self.timeObserverPlayer?.removeTimeObserver(timeObserver) - - self.timeObserver = nil - self.timeObserverPlayer = nil - } - if let didPlayToEndTimeObserver = self.didPlayToEndTimeObserver { - NotificationCenter.default.removeObserver(didPlayToEndTimeObserver) - self.didPlayToEndTimeObserver = nil - } - - self.audioDelayTimer?.invalidate() - self.audioDelayTimer = nil - } - - public func replaceSource(_ image: UIImage, additionalImage: UIImage?, time: CMTime) { + public func replaceSource(_ image: UIImage, additionalImage: UIImage?, time: CMTime, mirror: Bool) { guard let renderTarget = self.previewView, let device = renderTarget.mtlDevice, let texture = loadTexture(image: image, device: device) else { return } let additionalTexture = additionalImage.flatMap { loadTexture(image: $0, device: device) } - self.renderer.consumeTexture(texture, additionalTexture: additionalTexture, time: time, render: true) + if mirror { + self.renderer.videoFinishPass.additionalTextureRotation = .rotate0DegreesMirrored + } + self.renderer.consume(main: .texture(texture, time), additional: additionalTexture.flatMap { .texture($0, time) }, render: true, displayEnabled: false) } - private var volumeFade: SwiftSignalKit.Timer? private func setupSource() { guard let renderTarget = self.previewView else { return @@ -393,11 +467,11 @@ public final class MediaEditor { let context = self.context let clock = self.clock - let textureSource: Signal<(TextureSource, UIImage?, AVPlayer?, AVPlayer?, UIColor, UIColor), NoError> + let textureSource: Signal<(UIImage?, AVPlayer?, AVPlayer?, GradientColors), NoError> switch subject { case let .image(image, _): let colors = mediaEditorGetGradientColors(from: image) - textureSource = .single((ImageTextureSource(image: image, renderTarget: renderTarget), image, nil, nil, colors.0, colors.1)) + textureSource = .single((image, nil, nil, colors)) case let .draft(draft): if draft.isVideo { textureSource = Signal { subscriber in @@ -414,8 +488,8 @@ public final class MediaEditor { player.automaticallyWaitsToMinimizeStalling = false if let gradientColors = draft.values.gradientColors { - let colors = (gradientColors.first!, gradientColors.last!) - subscriber.putNext((VideoTextureSource(player: player, additionalPlayer: nil, mirror: false, renderTarget: renderTarget), nil, player, nil, colors.0, colors.1)) + let colors = GradientColors(top: gradientColors.first!, bottom: gradientColors.last!) + subscriber.putNext((nil, player, nil, colors)) subscriber.putCompletion() return EmptyDisposable @@ -424,12 +498,8 @@ public final class MediaEditor { imageGenerator.appliesPreferredTrackTransform = true imageGenerator.maximumSize = CGSize(width: 72, height: 128) imageGenerator.generateCGImagesAsynchronously(forTimes: [NSValue(time: CMTime(seconds: 0, preferredTimescale: CMTimeScale(30.0)))]) { _, image, _, _, _ in - if let image { - let colors = mediaEditorGetGradientColors(from: UIImage(cgImage: image)) - subscriber.putNext((VideoTextureSource(player: player, additionalPlayer: nil, mirror: false, renderTarget: renderTarget), nil, player, nil, colors.0, colors.1)) - } else { - subscriber.putNext((VideoTextureSource(player: player, additionalPlayer: nil, mirror: false, renderTarget: renderTarget), nil, player, nil, .black, .black)) - } + let colors: GradientColors = image.flatMap({ mediaEditorGetGradientColors(from: UIImage(cgImage: $0)) }) ?? GradientColors(top: .black, bottom: .black) + subscriber.putNext((nil, player, nil, colors)) subscriber.putCompletion() } return ActionDisposable { @@ -441,15 +511,16 @@ public final class MediaEditor { guard let image = UIImage(contentsOfFile: draft.fullPath(engine: context.engine)) else { return } - let colors: (UIColor, UIColor) + let colors: GradientColors if let gradientColors = draft.values.gradientColors { - colors = (gradientColors.first!, gradientColors.last!) + colors = GradientColors(top: gradientColors.first!, bottom: gradientColors.last!) } else { colors = mediaEditorGetGradientColors(from: image) } - textureSource = .single((ImageTextureSource(image: image, renderTarget: renderTarget), image, nil, nil, colors.0, colors.1)) + textureSource = .single((image, nil, nil, colors)) } - case let .video(path, transitionImage, mirror, additionalPath, _, _): + case let .video(path, transitionImage, mirror, _, _, _): + let _ = mirror textureSource = Signal { subscriber in let asset = AVURLAsset(url: URL(fileURLWithPath: path)) let player = AVPlayer(playerItem: AVPlayerItem(asset: asset)) @@ -460,21 +531,22 @@ public final class MediaEditor { } player.automaticallyWaitsToMinimizeStalling = false - var additionalPlayer: AVPlayer? - if let additionalPath { - let additionalAsset = AVURLAsset(url: URL(fileURLWithPath: additionalPath)) - additionalPlayer = AVPlayer(playerItem: AVPlayerItem(asset: additionalAsset)) - if #available(iOS 15.0, *) { - additionalPlayer?.sourceClock = clock - } else { - additionalPlayer?.masterClock = clock - } - additionalPlayer?.automaticallyWaitsToMinimizeStalling = false - } +// var additionalPlayer: AVPlayer? +// if let additionalPath { +// let additionalAsset = AVURLAsset(url: URL(fileURLWithPath: additionalPath)) +// additionalPlayer = AVPlayer(playerItem: AVPlayerItem(asset: additionalAsset)) +// if #available(iOS 15.0, *) { +// additionalPlayer?.sourceClock = clock +// } else { +// additionalPlayer?.masterClock = clock +// } +// additionalPlayer?.automaticallyWaitsToMinimizeStalling = false +// } if let transitionImage { let colors = mediaEditorGetGradientColors(from: transitionImage) - subscriber.putNext((VideoTextureSource(player: player, additionalPlayer: additionalPlayer, mirror: mirror, renderTarget: renderTarget), nil, player, additionalPlayer, colors.0, colors.1)) + //TODO pass mirror + subscriber.putNext((nil, player, nil, colors)) subscriber.putCompletion() return EmptyDisposable @@ -483,12 +555,9 @@ public final class MediaEditor { imageGenerator.appliesPreferredTrackTransform = true imageGenerator.maximumSize = CGSize(width: 72, height: 128) imageGenerator.generateCGImagesAsynchronously(forTimes: [NSValue(time: CMTime(seconds: 0, preferredTimescale: CMTimeScale(30.0)))]) { _, image, _, _, _ in - if let image { - let colors = mediaEditorGetGradientColors(from: UIImage(cgImage: image)) - subscriber.putNext((VideoTextureSource(player: player, additionalPlayer: additionalPlayer, mirror: mirror, renderTarget: renderTarget), nil, player, additionalPlayer, colors.0, colors.1)) - } else { - subscriber.putNext((VideoTextureSource(player: player, additionalPlayer: additionalPlayer, mirror: mirror, renderTarget: renderTarget), nil, player, additionalPlayer, .black, .black)) - } + let colors: GradientColors = image.flatMap({ mediaEditorGetGradientColors(from: UIImage(cgImage: $0)) }) ?? GradientColors(top: .black, bottom: .black) + //TODO pass mirror + subscriber.putNext((nil, player, nil, colors)) subscriber.putCompletion() } return ActionDisposable { @@ -515,7 +584,15 @@ public final class MediaEditor { let playerItem = AVPlayerItem(asset: asset) let player = AVPlayer(playerItem: playerItem) player.automaticallyWaitsToMinimizeStalling = false - subscriber.putNext((VideoTextureSource(player: player, additionalPlayer: nil, mirror: false, renderTarget: renderTarget), nil, player, nil, colors.0, colors.1)) + + #if targetEnvironment(simulator) + let additionalPlayerItem = AVPlayerItem(asset: asset) + let additionalPlayer = AVPlayer(playerItem: additionalPlayerItem) + additionalPlayer.automaticallyWaitsToMinimizeStalling = false + subscriber.putNext((nil, player, additionalPlayer, colors)) + #else + subscriber.putNext((nil, player, nil, colors)) + #endif subscriber.putCompletion() } }) @@ -541,7 +618,7 @@ public final class MediaEditor { } if !degraded { let colors = mediaEditorGetGradientColors(from: image) - subscriber.putNext((ImageTextureSource(image: image, renderTarget: renderTarget), image, nil, nil, colors.0, colors.1)) + subscriber.putNext((image, nil, nil, colors)) subscriber.putCompletion() } } @@ -556,17 +633,32 @@ public final class MediaEditor { self.textureSourceDisposable = (textureSource |> deliverOnMainQueue).start(next: { [weak self] sourceAndColors in if let self { - let (source, image, player, additionalPlayer, topColor, bottomColor) = sourceAndColors + let (image, player, additionalPlayer, colors) = sourceAndColors self.renderer.onNextRender = { [weak self] in self?.onFirstDisplay() } - self.renderer.textureSource = source + + let textureSource = UniversalTextureSource(renderTarget: renderTarget) + self.player = player - self.additionalPlayer = additionalPlayer - self.playerPromise.set(.single(player)) - self.gradientColorsValue = (topColor, bottomColor) - self.setGradientColors([topColor, bottomColor]) + + self.additionalPlayer = additionalPlayer + self.additionalPlayerPromise.set(.single(additionalPlayer)) + + if let image { + textureSource.setMainInput(.image(image)) + } + if let player, let playerItem = player.currentItem { + textureSource.setMainInput(.video(playerItem)) + } + if let additionalPlayer, let playerItem = additionalPlayer.currentItem { + textureSource.setAdditionalInput(.video(playerItem)) + } + self.renderer.textureSource = textureSource + + self.gradientColorsValue = colors + self.setGradientColors(colors.array) if player == nil { self.updateRenderChain() @@ -582,8 +674,8 @@ public final class MediaEditor { if let player { player.isMuted = self.values.videoIsMuted if let trimRange = self.values.videoTrimRange { - self.player?.currentItem?.forwardPlaybackEndTime = CMTime(seconds: trimRange.upperBound, preferredTimescale: CMTimeScale(1000)) - self.additionalPlayer?.currentItem?.forwardPlaybackEndTime = CMTime(seconds: trimRange.upperBound, preferredTimescale: CMTimeScale(1000)) + player.currentItem?.forwardPlaybackEndTime = CMTime(seconds: trimRange.upperBound, preferredTimescale: CMTimeScale(1000)) + additionalPlayer?.currentItem?.forwardPlaybackEndTime = CMTime(seconds: trimRange.upperBound, preferredTimescale: CMTimeScale(1000)) } if let initialSeekPosition = self.initialSeekPosition { @@ -600,7 +692,7 @@ public final class MediaEditor { additionalPlayer?.playImmediately(atRate: 1.0) self.audioPlayer?.playImmediately(atRate: 1.0) self.onPlaybackAction(.play) - self.volumeFade = self.player?.fadeVolume(from: 0.0, to: 1.0, duration: 0.4) + self.volumeFadeIn = player.fadeVolume(from: 0.0, to: 1.0, duration: 0.4) } if let audioPlayer = self.audioPlayer, audioPlayer.status != .readyToPlay { Queue.mainQueue().after(0.1) { @@ -628,14 +720,17 @@ public final class MediaEditor { }) } + public func setOnNextAdditionalDisplay(_ f: @escaping () -> Void) { + self.renderer.onNextAdditionalRender = f + } + private func setupTimeObservers() { var observedPlayer = self.player - var isAudioPlayerOnly = false + if observedPlayer == nil { + observedPlayer = self.additionalPlayer + } if observedPlayer == nil { observedPlayer = self.audioPlayer - if observedPlayer != nil { - isAudioPlayerOnly = true - } } guard let observedPlayer else { return @@ -654,7 +749,7 @@ public final class MediaEditor { if time.seconds > 20000 { } else { - self.playerPlaybackState = (duration, time.seconds, observedPlayer.rate > 0.0, hasAudio, isAudioPlayerOnly) + self.playerPlaybackState = PlaybackState(duration: duration, position: time.seconds, isPlaying: observedPlayer.rate > 0.0, hasAudio: hasAudio) } } } @@ -662,48 +757,42 @@ public final class MediaEditor { if self.didPlayToEndTimeObserver == nil { self.didPlayToEndTimeObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: observedPlayer.currentItem, queue: nil, using: { [weak self] notification in if let self { - var start: Double - if self.sourceIsVideo { + var start: Double = 0.0 + if self.player != nil { start = self.values.videoTrimRange?.lowerBound ?? 0.0 - } else { + } else if self.additionalPlayer != nil { + start = self.values.additionalVideoTrimRange?.lowerBound ?? 0.0 + } else if self.audioPlayer != nil { start = (self.values.audioTrackOffset ?? 0.0) + (self.values.audioTrackTrimRange?.lowerBound ?? 0.0) } - let targetTime = CMTime(seconds: start, preferredTimescale: CMTimeScale(1000)) - self.player?.seek(to: targetTime) - self.additionalPlayer?.seek(to: targetTime) - self.onPlaybackAction(.seek(start)) - - self.player?.play() - self.additionalPlayer?.play() - if self.sourceIsVideo { - let audioTime = self.audioTime(for: targetTime) - if let audioDelay = self.audioDelay(for: targetTime) { - self.audioPlayer?.pause() - self.audioDelayTimer = SwiftSignalKit.Timer(timeout: audioDelay, repeat: false, completion: { [weak self] in - self?.audioPlayer?.seek(to: audioTime, toleranceBefore: .zero, toleranceAfter: .zero) - self?.audioPlayer?.play() - }, queue: Queue.mainQueue()) - self.audioDelayTimer?.start() - } else { - self.audioPlayer?.seek(to: audioTime) - self.audioPlayer?.play() - } - } else { - self.audioPlayer?.seek(to: targetTime) - self.audioPlayer?.play() - } + self.player?.pause() + self.additionalPlayer?.pause() + self.audioPlayer?.pause() - Queue.mainQueue().justDispatch { - self.onPlaybackAction(.play) - } + self.seek(start, andPlay: true) } }) } } - private func setupDidPlayToEndObserver() { - + private func invalidateTimeObservers() { + if let timeObserver = self.timeObserver { + self.timeObserverPlayer?.removeTimeObserver(timeObserver) + + self.timeObserver = nil + self.timeObserverPlayer = nil + } + if let didPlayToEndTimeObserver = self.didPlayToEndTimeObserver { + NotificationCenter.default.removeObserver(didPlayToEndTimeObserver) + self.didPlayToEndTimeObserver = nil + } + + self.videoDelayTimer?.invalidate() + self.videoDelayTimer = nil + + self.audioDelayTimer?.invalidate() + self.audioDelayTimer = nil } public func attachPreviewView(_ previewView: MediaEditorPreviewView) { @@ -741,7 +830,7 @@ public final class MediaEditor { } public func setCrop(offset: CGPoint, scale: CGFloat, rotation: CGFloat, mirroring: Bool) { - self.updateValues(mode: .skipRendering) { values in + self.updateValues(mode: .forceRendering) { values in return values.withUpdatedCrop(offset: offset, scale: scale, rotation: rotation, mirroring: mirroring) } } @@ -764,6 +853,48 @@ public final class MediaEditor { } } + private var hadSound = false + public func maybeMuteVideo() { + guard let player = self.player else { + return + } + if !player.isMuted { + self.hadSound = true + player.isMuted = true + } + } + + public func maybeUnmuteVideo() { + guard let player = self.player else { + return + } + if self.hadSound { + self.hadSound = false + player.isMuted = false + } + } + + private var wasPlaying = false + @discardableResult + public func maybePauseVideo() -> Bool { + if self.isPlaying { + self.wasPlaying = true + self.stop(isInternal: true) + return true + } + return false + } + + @discardableResult + public func maybeUnpauseVideo() -> Bool { + if self.wasPlaying { + self.wasPlaying = false + self.play(isInternal: true) + return true + } + return false + } + public func setVideoIsMuted(_ videoIsMuted: Bool) { self.player?.isMuted = videoIsMuted self.updateValues(mode: .skipRendering) { values in @@ -771,6 +902,26 @@ public final class MediaEditor { } } + public func setVideoVolume(_ volume: CGFloat?) { + self.updateValues(mode: .skipRendering) { values in + return values.withUpdatedVideoVolume(volume) + } + + let audioMix: AVMutableAudioMix + if let current = self.playerAudioMix { + audioMix = current + } else { + audioMix = AVMutableAudioMix() + self.playerAudioMix = audioMix + } + if let asset = self.player?.currentItem?.asset { + let audioMixInputParameters = AVMutableAudioMixInputParameters(track: asset.tracks(withMediaType: .audio).first) + audioMixInputParameters.setVolume(Float(volume ?? 1.0), at: .zero) + audioMix.inputParameters = [audioMixInputParameters] + self.player?.currentItem?.audioMix = audioMix + } + } + public func setVideoIsMirrored(_ videoIsMirrored: Bool) { self.updateValues(mode: .skipRendering) { values in return values.withUpdatedVideoIsMirrored(videoIsMirrored) @@ -795,10 +946,11 @@ public final class MediaEditor { private var targetTimePosition: (CMTime, Bool)? private var updatingTimePosition = false public func seek(_ position: Double, andPlay play: Bool) { - if self.player == nil && self.audioPlayer == nil { + if self.player == nil && self.additionalPlayer == nil && self.audioPlayer == nil { self.initialSeekPosition = position return } + self.renderer.setRate(1.0) if !play { self.player?.pause() self.additionalPlayer?.pause() @@ -814,23 +966,42 @@ public final class MediaEditor { } if play { self.player?.play() - self.additionalPlayer?.play() - - if self.sourceIsVideo { - let audioTime = self.audioTime(for: targetPosition) - if let audioDelay = self.audioDelay(for: targetPosition) { - self.audioDelayTimer = SwiftSignalKit.Timer(timeout: audioDelay, repeat: false, completion: { [weak self] in - self?.audioPlayer?.seek(to: audioTime, toleranceBefore: .zero, toleranceAfter: .zero) - self?.audioPlayer?.play() - }, queue: Queue.mainQueue()) - self.audioDelayTimer?.start() - } else { - self.audioPlayer?.seek(to: audioTime, toleranceBefore: .zero, toleranceAfter: .zero) - self.audioPlayer?.play() - } - } else { + + if self.player == nil && self.additionalPlayer == nil { self.audioPlayer?.seek(to: targetPosition, toleranceBefore: .zero, toleranceAfter: .zero) self.audioPlayer?.play() + } else { + if let _ = self.additionalPlayer { + if self.player != nil { + let videoTime = self.videoTime(for: targetPosition) + if let videoDelay = self.videoDelay(for: targetPosition) { + self.videoDelayTimer = SwiftSignalKit.Timer(timeout: videoDelay, repeat: false, completion: { [weak self] in + self?.additionalPlayer?.seek(to: videoTime, toleranceBefore: .zero, toleranceAfter: .zero) + self?.additionalPlayer?.play() + }, queue: Queue.mainQueue()) + self.videoDelayTimer?.start() + } else { + self.additionalPlayer?.seek(to: videoTime, toleranceBefore: .zero, toleranceAfter: .zero) + self.additionalPlayer?.play() + } + } else { + self.additionalPlayer?.play() + } + } + + if let _ = self.audioPlayer { + let audioTime = self.audioTime(for: targetPosition) + if let audioDelay = self.audioDelay(for: targetPosition) { + self.audioDelayTimer = SwiftSignalKit.Timer(timeout: audioDelay, repeat: false, completion: { [weak self] in + self?.audioPlayer?.seek(to: audioTime, toleranceBefore: .zero, toleranceAfter: .zero) + self?.audioPlayer?.play() + }, queue: Queue.mainQueue()) + self.audioDelayTimer?.start() + } else { + self.audioPlayer?.seek(to: audioTime, toleranceBefore: .zero, toleranceAfter: .zero) + self.audioPlayer?.play() + } + } } self.onPlaybackAction(.play) @@ -852,11 +1023,13 @@ public final class MediaEditor { completion() } }) - - self.additionalPlayer?.seek(to: targetPosition, toleranceBefore: .zero, toleranceAfter: .zero) + if let _ = self.videoDelay(for: targetPosition) { + } else { + self.additionalPlayer?.seek(to: self.videoTime(for: targetPosition), toleranceBefore: .zero, toleranceAfter: .zero) + } + if let _ = self.audioDelay(for: targetPosition) { - } else { self.audioPlayer?.seek(to: self.audioTime(for: targetPosition), toleranceBefore: .zero, toleranceAfter: .zero) } @@ -868,7 +1041,12 @@ public final class MediaEditor { if time == .invalid { time = .zero } - let videoStart = self.values.videoTrimRange?.lowerBound ?? 0.0 + var videoStart: Double = 0.0 + if self.player != nil { + videoStart = self.values.videoTrimRange?.lowerBound ?? 0.0 + } else if self.additionalPlayer != nil { + videoStart = self.values.additionalVideoTrimRange?.lowerBound ?? 0.0 + } var audioStart = self.values.audioTrackTrimRange?.lowerBound ?? 0.0 if let offset = self.values.audioTrackOffset, offset < 0.0 { audioStart -= offset @@ -899,8 +1077,52 @@ public final class MediaEditor { } } + private var videoDelayTimer: SwiftSignalKit.Timer? + private func videoDelay(for time: CMTime) -> Double? { + var time = time + if time == .invalid { + time = .zero + } + let mainStart = self.values.videoTrimRange?.lowerBound ?? 0.0 + var trackStart = self.values.additionalVideoTrimRange?.lowerBound ?? 0.0 + if let offset = self.values.additionalVideoOffset, offset < 0.0 { + trackStart -= offset + } + if trackStart - mainStart > 0.0 { + let delay = trackStart - time.seconds + if delay > 0 { + return delay + } + } + return nil + } + + private func videoTime(for time: CMTime) -> CMTime { + var time = time + if time == .invalid { + time = .zero + } + let seconds = time.seconds + + let offset = self.values.additionalVideoOffset ?? 0.0 + let trackOffset = max(0.0, offset) + let trackStart = self.values.additionalVideoTrimRange?.lowerBound ?? 0.0 + if seconds < trackStart - min(0.0, offset) { + return CMTime(seconds: trackOffset + trackStart, preferredTimescale: CMTimeScale(1000.0)) + } else { + return CMTime(seconds: trackOffset + seconds + min(0.0, offset), preferredTimescale: CMTimeScale(1000.0)) + } + } + public var isPlaying: Bool { - let effectivePlayer = self.player ?? self.audioPlayer + var effectivePlayer: AVPlayer? + if let player = self.player { + effectivePlayer = player + } else if let additionalPlayer = self.additionalPlayer { + effectivePlayer = additionalPlayer + } else if let audioPlayer = self.audioPlayer { + effectivePlayer = audioPlayer + } return (effectivePlayer?.rate ?? 0.0) > 0.0 } @@ -913,13 +1135,38 @@ public final class MediaEditor { } public func play() { - self.setRate(1.0) + self.play(isInternal: false) } public func stop() { + self.stop(isInternal: false) + } + + private func play(isInternal: Bool) { + if !isInternal { + self.wasPlaying = false + } + self.setRate(1.0) + } + + private func stop(isInternal: Bool) { + if !isInternal { + self.wasPlaying = false + } self.setRate(0.0) } + public var mainFramerate: Float? { + if let player = self.player, let asset = player.currentItem?.asset, let track = asset.tracks(withMediaType: .video).first { + if track.nominalFrameRate > 0.0 { + return track.nominalFrameRate + } else if track.minFrameDuration.seconds > 0.0 { + return Float(1.0 / track.minFrameDuration.seconds) + } + } + return nil + } + private func setRate(_ rate: Float) { let hostTime: UInt64 = mach_absolute_time() let time: TimeInterval = 0 @@ -927,7 +1174,7 @@ public final class MediaEditor { let cmVTime = CMTimeMakeWithSeconds(time, preferredTimescale: 1000000) let futureTime = CMTimeAdd(cmHostTime, cmVTime) - if self.player == nil, let audioPlayer = self.audioPlayer { + if self.player == nil && self.additionalPlayer == nil, let audioPlayer = self.audioPlayer { let itemTime = audioPlayer.currentItem?.currentTime() ?? .invalid if audioPlayer.status == .readyToPlay { audioPlayer.setRate(rate, time: itemTime, atHostTime: futureTime) @@ -940,43 +1187,81 @@ public final class MediaEditor { } } } else { - let itemTime = self.player?.currentItem?.currentTime() ?? .invalid - let audioTime = self.audioTime(for: itemTime) - + var itemTime = self.player?.currentItem?.currentTime() ?? .invalid self.player?.setRate(rate, time: itemTime, atHostTime: futureTime) - self.additionalPlayer?.setRate(rate, time: itemTime, atHostTime: futureTime) - if let audioPlayer = self.audioPlayer { - if rate > 0.0, let audioDelay = self.audioDelay(for: itemTime) { - self.audioDelayTimer = SwiftSignalKit.Timer(timeout: audioDelay, repeat: false, completion: { [weak self] in - self?.audioPlayer?.seek(to: audioTime, toleranceBefore: .zero, toleranceAfter: .zero) - self?.audioPlayer?.play() - }, queue: Queue.mainQueue()) - self.audioDelayTimer?.start() + if let additionalPlayer = self.additionalPlayer { + if self.player != nil { + let videoTime = self.videoTime(for: itemTime) + if rate > 0.0 { + if let videoDelay = self.videoDelay(for: itemTime) { + self.videoDelayTimer = SwiftSignalKit.Timer(timeout: videoDelay, repeat: false, completion: { [weak self] in + self?.additionalPlayer?.seek(to: videoTime, toleranceBefore: .zero, toleranceAfter: .zero) + self?.additionalPlayer?.play() + }, queue: Queue.mainQueue()) + self.videoDelayTimer?.start() + } else { + if additionalPlayer.status == .readyToPlay { + additionalPlayer.setRate(rate, time: videoTime, atHostTime: futureTime) + additionalPlayer.play() + } else { + additionalPlayer.seek(to: videoTime, toleranceBefore: .zero, toleranceAfter: .zero) + additionalPlayer.play() + } + } + } else { + additionalPlayer.pause() + } } else { - if audioPlayer.status == .readyToPlay { - audioPlayer.setRate(rate, time: audioTime, atHostTime: futureTime) - if rate > 0.0 { -// audioPlayer.seek(to: audioTime, toleranceBefore: .zero, toleranceAfter: .zero) - audioPlayer.play() + itemTime = additionalPlayer.currentItem?.currentTime() ?? .invalid + if itemTime != .invalid { + if additionalPlayer.status == .readyToPlay { + additionalPlayer.setRate(rate, time: itemTime, atHostTime: futureTime) + } else { + additionalPlayer.seek(to: itemTime, toleranceBefore: .zero, toleranceAfter: .zero) + if rate > 0.0 { + additionalPlayer.play() + } else { + additionalPlayer.pause() + } } + } + } + } + + if let audioPlayer = self.audioPlayer { + let audioTime = self.audioTime(for: itemTime) + if rate > 0.0 { + if let audioDelay = self.audioDelay(for: itemTime) { + self.audioDelayTimer = SwiftSignalKit.Timer(timeout: audioDelay, repeat: false, completion: { [weak self] in + self?.audioPlayer?.seek(to: audioTime, toleranceBefore: .zero, toleranceAfter: .zero) + self?.audioPlayer?.play() + }, queue: Queue.mainQueue()) + self.audioDelayTimer?.start() } else { - audioPlayer.seek(to: audioTime, toleranceBefore: .zero, toleranceAfter: .zero) - if rate > 0.0 { + if audioPlayer.status == .readyToPlay { + audioPlayer.setRate(rate, time: audioTime, atHostTime: futureTime) audioPlayer.play() } else { - audioPlayer.pause() + audioPlayer.seek(to: audioTime, toleranceBefore: .zero, toleranceAfter: .zero) + audioPlayer.play() } } + } else { + audioPlayer.pause() } } } + self.renderer.setRate(rate) if rate > 0.0 { self.onPlaybackAction(.play) } else { self.onPlaybackAction(.pause) + self.videoDelayTimer?.invalidate() + self.videoDelayTimer = nil + self.audioDelayTimer?.invalidate() self.audioDelayTimer = nil } @@ -991,6 +1276,9 @@ public final class MediaEditor { self.audioDelayTimer?.invalidate() self.audioDelayTimer = nil + + self.videoDelayTimer?.invalidate() + self.videoDelayTimer = nil } private func updateVideoTimePosition() { @@ -999,7 +1287,7 @@ public final class MediaEditor { } self.updatingTimePosition = true - if self.player == nil, let audioPlayer = self.audioPlayer { + if self.player == nil && self.additionalPlayer == nil, let audioPlayer = self.audioPlayer { audioPlayer.seek(to: targetPosition, toleranceBefore: .zero, toleranceAfter: .zero, completionHandler: { [weak self] _ in if let self { if let (currentTargetPosition, _) = self.targetTimePosition, currentTargetPosition == targetPosition { @@ -1022,8 +1310,26 @@ public final class MediaEditor { } }) - self.additionalPlayer?.seek(to: targetPosition, toleranceBefore: .zero, toleranceAfter: .zero) - + if let additionalPlayer = self.additionalPlayer { + if self.player != nil { + if let _ = self.videoDelay(for: targetPosition) { + } else { + self.additionalPlayer?.seek(to: self.videoTime(for: targetPosition), toleranceBefore: .zero, toleranceAfter: .zero) + } + } else { + additionalPlayer.seek(to: targetPosition, toleranceBefore: .zero, toleranceAfter: .zero, completionHandler: { [weak self] _ in + if let self { + if let (currentTargetPosition, _) = self.targetTimePosition, currentTargetPosition == targetPosition { + self.updatingTimePosition = false + self.targetTimePosition = nil + } else { + self.updateVideoTimePosition() + } + } + }) + } + } + if let _ = self.audioDelay(for: targetPosition) { } else { self.audioPlayer?.seek(to: self.audioTime(for: targetPosition), toleranceBefore: .zero, toleranceAfter: .zero) @@ -1039,14 +1345,81 @@ public final class MediaEditor { if apply { self.player?.currentItem?.forwardPlaybackEndTime = CMTime(seconds: trimRange.upperBound, preferredTimescale: CMTimeScale(1000)) - self.additionalPlayer?.currentItem?.forwardPlaybackEndTime = CMTime(seconds: trimRange.upperBound, preferredTimescale: CMTimeScale(1000)) +// self.additionalPlayer?.currentItem?.forwardPlaybackEndTime = CMTime(seconds: trimRange.upperBound, preferredTimescale: CMTimeScale(1000)) } } - public func setAdditionalVideo(_ path: String, positionChanges: [VideoPositionChange]) { + public func setAdditionalVideo(_ path: String?, isDual: Bool = false, positionChanges: [VideoPositionChange]) { self.updateValues(mode: .skipRendering) { values in - return values.withUpdatedAdditionalVideo(path: path, positionChanges: positionChanges) + var values = values.withUpdatedAdditionalVideo(path: path, isDual: isDual, positionChanges: positionChanges) + if path == nil { + values = values.withUpdatedAdditionalVideoOffset(nil).withUpdatedAdditionalVideoTrimRange(nil).withUpdatedAdditionalVideoVolume(nil) + } + return values + } + + if let additionalPlayer = self.additionalPlayer { + additionalPlayer.pause() + + self.additionalPlayer = nil + self.additionalPlayerPromise.set(.single(nil)) + self.additionalPlayerAudioMix = nil + + if let textureSource = self.renderer.textureSource as? UniversalTextureSource { + textureSource.forceUpdates = true + self.renderer.videoFinishPass.animateAdditionalRemoval { [weak textureSource] in + if let textureSource { + textureSource.setAdditionalInput(nil) + textureSource.forceUpdates = false + } + } + } + + self.videoDelayTimer?.invalidate() + self.videoDelayTimer = nil + + if self.player == nil { + self.invalidateTimeObservers() + } + } + + self.setupAdditionalVideoPlayback() + self.updateAdditionalVideoPlaybackRange() + + if self.player == nil { + self.invalidateTimeObservers() + self.setupTimeObservers() + self.additionalPlayer?.play() + } + } + + private func setupAdditionalVideoPlayback() { + guard let additionalVideoPath = self.values.additionalVideoPath else { + return + } + let asset = AVURLAsset(url: URL(fileURLWithPath: additionalVideoPath)) + let playerItem = AVPlayerItem(asset: asset) + let player = AVPlayer(playerItem: playerItem) + if #available(iOS 15.0, *) { + player.sourceClock = clock + } else { + player.masterClock = clock + } + player.automaticallyWaitsToMinimizeStalling = false + + let audioMix = AVMutableAudioMix() + let audioMixInputParameters = AVMutableAudioMixInputParameters(track: asset.tracks(withMediaType: .audio).first) + if let volume = self.values.additionalVideoVolume { + audioMixInputParameters.setVolume(Float(volume), at: .zero) } + audioMix.inputParameters = [audioMixInputParameters] + player.currentItem?.audioMix = audioMix + + self.additionalPlayer = player + self.additionalPlayerPromise.set(.single(player)) + self.additionalPlayerAudioMix = audioMix + + (self.renderer.textureSource as? UniversalTextureSource)?.setAdditionalInput(.video(playerItem)) } public func setAdditionalVideoPosition(_ position: CGPoint, scale: CGFloat, rotation: CGFloat) { @@ -1055,36 +1428,70 @@ public final class MediaEditor { } } - public func setDrawingAndEntities(data: Data?, image: UIImage?, entities: [CodableDrawingEntity]) { - self.updateValues(mode: .skipRendering) { values in - return values.withUpdatedDrawingAndEntities(drawing: image, entities: entities) + public func setAdditionalVideoTrimRange(_ trimRange: Range, apply: Bool) { + self.updateValues(mode: .generic) { values in + return values.withUpdatedAdditionalVideoTrimRange(trimRange) + } + + if apply { + self.updateAdditionalVideoPlaybackRange() } } - public func setGradientColors(_ gradientColors: [UIColor]) { + public func setAdditionalVideoOffset(_ offset: Double?, apply: Bool) { + self.updateValues(mode: .generic) { values in + return values.withUpdatedAdditionalVideoOffset(offset) + } + + if apply { + self.updateAdditionalVideoPlaybackRange() + } + } + + public func setAdditionalVideoVolume(_ volume: CGFloat?) { self.updateValues(mode: .skipRendering) { values in - return values.withUpdatedGradientColors(gradientColors: gradientColors) + return values.withUpdatedAdditionalVideoVolume(volume) + } + + if let audioMix = self.additionalPlayerAudioMix, let asset = self.additionalPlayer?.currentItem?.asset { + let audioMixInputParameters = AVMutableAudioMixInputParameters(track: asset.tracks(withMediaType: .audio).first) + audioMixInputParameters.setVolume(Float(volume ?? 1.0), at: .zero) + audioMix.inputParameters = [audioMixInputParameters] + self.additionalPlayer?.currentItem?.audioMix = audioMix } } + private func updateAdditionalVideoPlaybackRange() { + if let upperBound = self.values.additionalVideoTrimRange?.upperBound { + let offset = max(0.0, self.values.additionalVideoOffset ?? 0.0) + self.additionalPlayer?.currentItem?.forwardPlaybackEndTime = CMTime(seconds: offset + upperBound, preferredTimescale: CMTimeScale(1000)) + } else { + self.additionalPlayer?.currentItem?.forwardPlaybackEndTime = .invalid + } + } + public func setAudioTrack(_ audioTrack: MediaAudioTrack?, trimRange: Range? = nil, offset: Double? = nil) { self.updateValues(mode: .skipRendering) { values in - return values.withUpdatedAudioTrack(audioTrack).withUpdatedAudioTrackSamples(nil).withUpdatedAudioTrackTrimRange(trimRange).withUpdatedAudioTrackVolume(nil).withUpdatedAudioTrackOffset(offset) + return values + .withUpdatedAudioTrack(audioTrack) + .withUpdatedAudioTrackSamples(nil) + .withUpdatedAudioTrackTrimRange(trimRange) + .withUpdatedAudioTrackVolume(nil) + .withUpdatedAudioTrackOffset(offset) } if let audioPlayer = self.audioPlayer { audioPlayer.pause() - if self.sourceIsVideo { - self.audioDelayTimer?.invalidate() - self.audioDelayTimer = nil - } else { - self.destroyTimeObservers() - } self.audioPlayer = nil - - if !self.sourceIsVideo { - self.playerPromise.set(.single(nil)) + self.audioPlayerPromise.set(.single(nil)) + self.audioPlayerAudioMix = nil + + self.audioDelayTimer?.invalidate() + self.audioDelayTimer = nil + + if self.player == nil { + self.invalidateTimeObservers() } } @@ -1093,25 +1500,28 @@ public final class MediaEditor { } private func setupAudioPlayback() { - if let audioTrack = self.values.audioTrack { - let path = fullDraftPath(peerId: self.context.account.peerId, path: audioTrack.path) - let audioAsset = AVURLAsset(url: URL(fileURLWithPath: path)) - let playerItem = AVPlayerItem(asset: audioAsset) - let player = AVPlayer(playerItem: playerItem) - player.automaticallyWaitsToMinimizeStalling = false - self.audioPlayer = player - self.maybeGenerateAudioSamples(asset: audioAsset) - - if let volume = self.values.audioTrackVolume { - self.audioPlayer?.volume = Float(volume) - } - - self.setupTimeObservers() - - if !self.sourceIsVideo { - self.playerPromise.set(.single(player)) - } + guard let audioTrack = self.values.audioTrack else { + return } + let audioPath = fullDraftPath(peerId: self.context.account.peerId, path: audioTrack.path) + let audioAsset = AVURLAsset(url: URL(fileURLWithPath: audioPath)) + let audioPlayer = AVPlayer(playerItem: AVPlayerItem(asset: audioAsset)) + audioPlayer.automaticallyWaitsToMinimizeStalling = false + + let audioMix = AVMutableAudioMix() + let audioMixInputParameters = AVMutableAudioMixInputParameters(track: audioAsset.tracks(withMediaType: .audio).first) + if let volume = self.values.audioTrackVolume { + audioMixInputParameters.setVolume(Float(volume), at: .zero) + } + audioMix.inputParameters = [audioMixInputParameters] + audioPlayer.currentItem?.audioMix = audioMix + + self.audioPlayer = audioPlayer + self.audioPlayerPromise.set(.single(audioPlayer)) + self.audioPlayerAudioMix = audioMix + self.maybeGenerateAudioSamples(asset: audioAsset) + + self.setupTimeObservers() } public func setAudioTrackTrimRange(_ trimRange: Range?, apply: Bool) { @@ -1133,7 +1543,7 @@ public final class MediaEditor { self.updateAudioPlaybackRange() } } - + private func updateAudioPlaybackRange() { if let upperBound = self.values.audioTrackTrimRange?.upperBound { let offset = max(0.0, self.values.audioTrackOffset ?? 0.0) @@ -1148,15 +1558,32 @@ public final class MediaEditor { return values.withUpdatedAudioTrackVolume(volume) } - self.audioPlayer?.volume = Float(volume ?? 1.0) + if let audioMix = self.audioPlayerAudioMix, let asset = self.audioPlayer?.currentItem?.asset { + let audioMixInputParameters = AVMutableAudioMixInputParameters(track: asset.tracks(withMediaType: .audio).first) + audioMixInputParameters.setVolume(Float(volume ?? 1.0), at: .zero) + audioMix.inputParameters = [audioMixInputParameters] + self.audioPlayer?.currentItem?.audioMix = audioMix + } + } + + public func setDrawingAndEntities(data: Data?, image: UIImage?, entities: [CodableDrawingEntity]) { + self.updateValues(mode: .skipRendering) { values in + return values.withUpdatedDrawingAndEntities(drawing: image, entities: entities) + } + } + + public func setGradientColors(_ gradientColors: [UIColor]) { + self.updateValues(mode: .skipRendering) { values in + return values.withUpdatedGradientColors(gradientColors: gradientColors) + } } private var previousUpdateTime: Double? private var scheduledUpdate = false private func updateRenderChain() { - self.renderer.renderPassedEnabled = !self.previewUnedited + self.renderer.skipEditingPasses = self.previewUnedited self.renderChain.update(values: self.values) - self.renderer.videoFinishPass.update(values: self.values) + self.renderer.videoFinishPass.update(values: self.values, videoDuration: self.mainVideoDuration, additionalVideoDuration: self.additionalVideoDuration) if let player = self.player, player.rate > 0.0 && !self.forceRendering { } else { @@ -1224,7 +1651,6 @@ public final class MediaEditor { do { let assetReader = try AVAssetReader(asset: asset) - let settings: [String: Any] = [ AVFormatIDKey: kAudioFormatLinearPCM, AVLinearPCMBitDepthKey: 32, @@ -1234,9 +1660,7 @@ public final class MediaEditor { ] let assetReaderOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: settings) - assetReader.add(assetReaderOutput) - assetReader.startReading() var samplesData = Data() @@ -1269,3 +1693,89 @@ public final class MediaEditor { } } } + +private func videoFrames(asset: AVAsset, count: Int, mirror: Bool = false) -> Signal<([UIImage], Double), NoError> { + func blurredImage(_ image: UIImage) -> UIImage? { + guard let image = image.cgImage else { + return nil + } + + let thumbnailSize = CGSize(width: image.width, height: image.height) + let thumbnailContextSize = thumbnailSize.aspectFilled(CGSize(width: 20.0, height: 20.0)) + if let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0) { + thumbnailContext.withFlippedContext { c in + c.interpolationQuality = .none + c.draw(image, in: CGRect(origin: CGPoint(), size: thumbnailContextSize)) + } + imageFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) + + let thumbnailContext2Size = thumbnailSize.aspectFitted(CGSize(width: 100.0, height: 100.0)) + if let thumbnailContext2 = DrawingContext(size: thumbnailContext2Size, scale: 1.0) { + thumbnailContext2.withFlippedContext { c in + c.interpolationQuality = .none + if let image = thumbnailContext.generateImage()?.cgImage { + c.draw(image, in: CGRect(origin: CGPoint(), size: thumbnailContext2Size)) + } + } + imageFastBlur(Int32(thumbnailContext2Size.width), Int32(thumbnailContext2Size.height), Int32(thumbnailContext2.bytesPerRow), thumbnailContext2.bytes) + return thumbnailContext2.generateImage() + } + } + return nil + } + + guard count > 0 else { + return .complete() + } + let scale = UIScreen.main.scale + let imageGenerator = AVAssetImageGenerator(asset: asset) + imageGenerator.maximumSize = CGSize(width: 48.0 * scale, height: 36.0 * scale) + imageGenerator.appliesPreferredTrackTransform = true + imageGenerator.requestedTimeToleranceBefore = .zero + imageGenerator.requestedTimeToleranceAfter = .zero + + var firstFrame: UIImage + if let cgImage = try? imageGenerator.copyCGImage(at: .zero, actualTime: nil) { + firstFrame = UIImage(cgImage: cgImage) + if let blurred = blurredImage(firstFrame) { + firstFrame = blurred + } + } else { + firstFrame = generateSingleColorImage(size: CGSize(width: 24.0, height: 36.0), color: .black)! + } + return Signal { subscriber in + subscriber.putNext((Array(repeating: firstFrame, count: count), CACurrentMediaTime())) + + var timestamps: [NSValue] = [] + let duration = asset.duration.seconds + let interval = duration / Double(count) + for i in 0 ..< count { + timestamps.append(NSValue(time: CMTime(seconds: Double(i) * interval, preferredTimescale: CMTimeScale(1000)))) + } + + var updatedFrames: [UIImage] = [] + imageGenerator.generateCGImagesAsynchronously(forTimes: timestamps) { _, image, _, _, _ in + if let image { + updatedFrames.append(UIImage(cgImage: image, scale: 1.0, orientation: mirror ? .upMirrored : .up)) + if updatedFrames.count == count { + subscriber.putNext((updatedFrames, CACurrentMediaTime())) + subscriber.putCompletion() + } else { + var tempFrames = updatedFrames + for _ in 0 ..< count - updatedFrames.count { + tempFrames.append(firstFrame) + } + subscriber.putNext((tempFrames, CACurrentMediaTime())) + } + } else { + if let previous = updatedFrames.last { + updatedFrames.append(previous) + } + } + } + + return ActionDisposable { + imageGenerator.cancelAllCGImageGeneration() + } + } +} diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift index 75fd7e83c8e..73ac5d0cfd6 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift @@ -26,17 +26,43 @@ public func mediaEditorGenerateGradientImage(size: CGSize, colors: [UIColor]) -> return image } -public func mediaEditorGetGradientColors(from image: UIImage) -> (UIColor, UIColor) { +public func mediaEditorGetGradientColors(from image: UIImage) -> MediaEditor.GradientColors { let context = DrawingContext(size: CGSize(width: 5.0, height: 5.0), scale: 1.0, clear: false)! context.withFlippedContext({ context in if let cgImage = image.cgImage { context.draw(cgImage, in: CGRect(x: 0.0, y: 0.0, width: 5.0, height: 5.0)) } }) - return (context.colorAt(CGPoint(x: 2.0, y: 0.0)), context.colorAt(CGPoint(x: 2.0, y: 4.0))) + return MediaEditor.GradientColors( + top: context.colorAt(CGPoint(x: 2.0, y: 0.0)), + bottom: context.colorAt(CGPoint(x: 2.0, y: 4.0)) + ) } final class MediaEditorComposer { + enum Input { + case texture(MTLTexture, CMTime) + case videoBuffer(VideoPixelBuffer) + + var timestamp: CMTime { + switch self { + case let .texture(_, timestamp): + return timestamp + case let .videoBuffer(videoBuffer): + return videoBuffer.timestamp + } + } + + var rendererInput: MediaEditorRenderer.Input { + switch self { + case let .texture(texture, time): + return .texture(texture, time) + case let .videoBuffer(videoBuffer): + return .videoBuffer(videoBuffer) + } + } + } + let device: MTLDevice? private let colorSpace: CGColorSpace @@ -51,11 +77,18 @@ final class MediaEditorComposer { private let renderer = MediaEditorRenderer() private let renderChain = MediaEditorRenderChain() - private let gradientImage: CIImage private let drawingImage: CIImage? private var entities: [MediaEditorComposerEntity] - init(postbox: Postbox, values: MediaEditorValues, dimensions: CGSize, outputDimensions: CGSize, textScale: CGFloat) { + init( + postbox: Postbox, + values: MediaEditorValues, + dimensions: CGSize, + outputDimensions: CGSize, + textScale: CGFloat, + videoDuration: Double?, + additionalVideoDuration: Double? + ) { self.values = values self.dimensions = dimensions self.outputDimensions = outputDimensions @@ -66,12 +99,6 @@ final class MediaEditorComposer { self.renderer.addRenderChain(self.renderChain) - if let gradientColors = values.gradientColors, let image = mediaEditorGenerateGradientImage(size: dimensions, colors: gradientColors) { - self.gradientImage = CIImage(image: image, options: [.colorSpace: self.colorSpace])!.transformed(by: CGAffineTransform(translationX: -dimensions.width / 2.0, y: -dimensions.height / 2.0)) - } else { - self.gradientImage = CIImage(color: .black) - } - if let drawing = values.drawing, let drawingImage = CIImage(image: drawing, options: [.colorSpace: self.colorSpace]) { self.drawingImage = drawingImage.transformed(by: CGAffineTransform(translationX: -dimensions.width / 2.0, y: -dimensions.height / 2.0)) } else { @@ -95,70 +122,35 @@ final class MediaEditorComposer { self.renderer.setupForComposer(composer: self) self.renderChain.update(values: self.values) - self.renderer.videoFinishPass.update(values: self.values) + self.renderer.videoFinishPass.update(values: self.values, videoDuration: videoDuration, additionalVideoDuration: additionalVideoDuration) } - - func processSampleBuffer(sampleBuffer: CMSampleBuffer, textureRotation: TextureRotation, additionalSampleBuffer: CMSampleBuffer?, additionalTextureRotation: TextureRotation, pool: CVPixelBufferPool?, completion: @escaping (CVPixelBuffer?) -> Void) { - guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer), let pool = pool else { + + var previousAdditionalInput: Input? + func process(main: Input, additional: Input?, pool: CVPixelBufferPool?, completion: @escaping (CVPixelBuffer?) -> Void) { + guard let pool, let ciContext = self.ciContext else { completion(nil) return } - let time = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) - let mainPixelBuffer = VideoPixelBuffer(pixelBuffer: imageBuffer, rotation: textureRotation, timestamp: time) - var additionalPixelBuffer: VideoPixelBuffer? - if let additionalSampleBuffer, let additionalImageBuffer = CMSampleBufferGetImageBuffer(additionalSampleBuffer) { - additionalPixelBuffer = VideoPixelBuffer(pixelBuffer: additionalImageBuffer, rotation: additionalTextureRotation, timestamp: time) + var additional = additional + if let additional { + self.previousAdditionalInput = additional + } else { + additional = self.previousAdditionalInput } - self.renderer.consumeVideoPixelBuffer(pixelBuffer: mainPixelBuffer, additionalPixelBuffer: additionalPixelBuffer, render: true) - if let finalTexture = self.renderer.finalTexture, var ciImage = CIImage(mtlTexture: finalTexture, options: [.colorSpace: self.colorSpace]) { + self.renderer.consume(main: main.rendererInput, additional: additional?.rendererInput, render: true) + + if let resultTexture = self.renderer.resultTexture, var ciImage = CIImage(mtlTexture: resultTexture, options: [.colorSpace: self.colorSpace]) { ciImage = ciImage.transformed(by: CGAffineTransformMakeScale(1.0, -1.0).translatedBy(x: 0.0, y: -ciImage.extent.height)) var pixelBuffer: CVPixelBuffer? CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pool, &pixelBuffer) if let pixelBuffer { - processImage(inputImage: ciImage, time: time, completion: { compositedImage in - if var compositedImage { - let scale = self.outputDimensions.width / compositedImage.extent.width - compositedImage = compositedImage.transformed(by: CGAffineTransform(scaleX: scale, y: scale)) - - self.ciContext?.render(compositedImage, to: pixelBuffer) - completion(pixelBuffer) - } else { - completion(nil) - } - }) - return - } - } - completion(nil) - } - - private var filteredImage: CIImage? - func processImage(inputImage: UIImage, pool: CVPixelBufferPool?, time: CMTime, completion: @escaping (CVPixelBuffer?) -> Void) { - guard let pool else { - completion(nil) - return - } - if self.filteredImage == nil, let device = self.device { - if let texture = loadTexture(image: inputImage, device: device) { - self.renderer.consumeTexture(texture, render: true) + let time = main.timestamp - if let finalTexture = self.renderer.finalTexture, var ciImage = CIImage(mtlTexture: finalTexture, options: [.colorSpace: self.colorSpace]) { - ciImage = ciImage.transformed(by: CGAffineTransformMakeScale(1.0, -1.0).translatedBy(x: 0.0, y: -ciImage.extent.height)) - self.filteredImage = ciImage - } - } - } - - if let image = self.filteredImage { - var pixelBuffer: CVPixelBuffer? - CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pool, &pixelBuffer) - - if let pixelBuffer, let context = self.ciContext { - makeEditorImageFrameComposition(context: context, inputImage: image, gradientImage: self.gradientImage, drawingImage: self.drawingImage, dimensions: self.dimensions, outputDimensions: self.outputDimensions, values: self.values, entities: self.entities, time: time, completion: { compositedImage in + makeEditorImageFrameComposition(context: ciContext, inputImage: ciImage, drawingImage: self.drawingImage, dimensions: self.dimensions, outputDimensions: self.outputDimensions, values: self.values, entities: self.entities, time: time, completion: { compositedImage in if var compositedImage { let scale = self.outputDimensions.width / compositedImage.extent.width compositedImage = compositedImage.samplingLinear().transformed(by: CGAffineTransform(scaleX: scale, y: scale)) @@ -175,24 +167,23 @@ final class MediaEditorComposer { completion(nil) } - func processImage(inputImage: CIImage, time: CMTime, completion: @escaping (CIImage?) -> Void) { - guard let context = self.ciContext else { - return + private var cachedTexture: MTLTexture? + func textureForImage(_ image: UIImage) -> MTLTexture? { + if let cachedTexture = self.cachedTexture { + return cachedTexture + } + if let device = self.device, let texture = loadTexture(image: image, device: device) { + self.cachedTexture = texture + return texture } - makeEditorImageFrameComposition(context: context, inputImage: inputImage, gradientImage: self.gradientImage, drawingImage: self.drawingImage, dimensions: self.dimensions, outputDimensions: self.outputDimensions, values: self.values, entities: self.entities, time: time, textScale: self.textScale, completion: completion) + return nil } } public func makeEditorImageComposition(context: CIContext, postbox: Postbox, inputImage: UIImage, dimensions: CGSize, values: MediaEditorValues, time: CMTime, textScale: CGFloat, completion: @escaping (UIImage?) -> Void) { let colorSpace = CGColorSpaceCreateDeviceRGB() let inputImage = CIImage(image: inputImage, options: [.colorSpace: colorSpace])! - let gradientImage: CIImage var drawingImage: CIImage? - if let gradientColors = values.gradientColors, let image = mediaEditorGenerateGradientImage(size: dimensions, colors: gradientColors) { - gradientImage = CIImage(image: image, options: [.colorSpace: colorSpace])!.transformed(by: CGAffineTransform(translationX: -dimensions.width / 2.0, y: -dimensions.height / 2.0)) - } else { - gradientImage = CIImage(color: .black) - } if let drawing = values.drawing, let image = CIImage(image: drawing, options: [.colorSpace: colorSpace]) { drawingImage = image.transformed(by: CGAffineTransform(translationX: -dimensions.width / 2.0, y: -dimensions.height / 2.0)) @@ -203,7 +194,7 @@ public func makeEditorImageComposition(context: CIContext, postbox: Postbox, inp entities.append(contentsOf: composerEntitiesForDrawingEntity(postbox: postbox, textScale: textScale, entity: entity.entity, colorSpace: colorSpace)) } - makeEditorImageFrameComposition(context: context, inputImage: inputImage, gradientImage: gradientImage, drawingImage: drawingImage, dimensions: dimensions, outputDimensions: dimensions, values: values, entities: entities, time: time, textScale: textScale, completion: { ciImage in + makeEditorImageFrameComposition(context: context, inputImage: inputImage, drawingImage: drawingImage, dimensions: dimensions, outputDimensions: dimensions, values: values, entities: entities, time: time, textScale: textScale, completion: { ciImage in if let ciImage { if let cgImage = context.createCGImage(ciImage, from: CGRect(origin: .zero, size: ciImage.extent.size)) { Queue.mainQueue().async { @@ -216,11 +207,8 @@ public func makeEditorImageComposition(context: CIContext, postbox: Postbox, inp }) } -private func makeEditorImageFrameComposition(context: CIContext, inputImage: CIImage, gradientImage: CIImage, drawingImage: CIImage?, dimensions: CGSize, outputDimensions: CGSize, values: MediaEditorValues, entities: [MediaEditorComposerEntity], time: CMTime, textScale: CGFloat = 1.0, completion: @escaping (CIImage?) -> Void) { +private func makeEditorImageFrameComposition(context: CIContext, inputImage: CIImage, drawingImage: CIImage?, dimensions: CGSize, outputDimensions: CGSize, values: MediaEditorValues, entities: [MediaEditorComposerEntity], time: CMTime, textScale: CGFloat = 1.0, completion: @escaping (CIImage?) -> Void) { var resultImage = CIImage(color: .black).cropped(to: CGRect(origin: .zero, size: dimensions)).transformed(by: CGAffineTransform(translationX: -dimensions.width / 2.0, y: -dimensions.height / 2.0)) - if values.isStory { - resultImage = gradientImage.composited(over: resultImage) - } var mediaImage = inputImage.samplingLinear().transformed(by: CGAffineTransform(translationX: -inputImage.extent.midX, y: -inputImage.extent.midY)) @@ -232,11 +220,7 @@ private func makeEditorImageFrameComposition(context: CIContext, inputImage: CII } if values.isStory { - var cropTransform: CGAffineTransform = CGAffineTransform(translationX: values.cropOffset.x, y: values.cropOffset.y * -1.0) - cropTransform = cropTransform.rotated(by: -values.cropRotation) - cropTransform = cropTransform.scaledBy(x: initialScale * values.cropScale, y: initialScale * values.cropScale) - mediaImage = mediaImage.transformed(by: cropTransform) - resultImage = mediaImage.composited(over: resultImage) + resultImage = mediaImage.samplingLinear().composited(over: resultImage) } else { var horizontalScale = initialScale if values.cropMirroring { @@ -247,9 +231,6 @@ private func makeEditorImageFrameComposition(context: CIContext, inputImage: CII } if let drawingImage { -// if values.isStory { -// drawingImage = drawingImage.transformed(by: CGAffineTransformMakeScale(initialScale, initialScale)) -// } resultImage = drawingImage.samplingLinear().composited(over: resultImage) } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift index a6d865c56aa..397c299d5a1 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift @@ -65,7 +65,6 @@ func composerEntitiesForDrawingEntity(postbox: Postbox, textScale: CGFloat, enti if let entity = entity as? DrawingStickerEntity { if case let .file(_, type) = entity.content, case .reaction = type { return [] -// return [MediaEditorComposerStaticEntity(image: image, position: entity.position, scale: entity.scale, rotation: entity.rotation, baseSize: entity.baseSize, mirrored: false)] } else { let content: MediaEditorComposerStickerEntity.Content switch entity.content { diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorRenderer.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorRenderer.swift index c4f6f3b8486..0242d8dc452 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorRenderer.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorRenderer.swift @@ -21,11 +21,6 @@ final class VideoPixelBuffer { } } -protocol TextureConsumer: AnyObject { - func consumeTexture(_ texture: MTLTexture, render: Bool) - func consumeVideoPixelBuffer(pixelBuffer: VideoPixelBuffer, additionalPixelBuffer: VideoPixelBuffer?, render: Bool) -} - final class RenderingContext { let device: MTLDevice let commandBuffer: MTLCommandBuffer @@ -45,8 +40,10 @@ protocol RenderPass: AnyObject { } protocol TextureSource { - func connect(to: TextureConsumer) + func connect(to renderer: MediaEditorRenderer) func invalidate() + + func setRate(_ rate: Float) } protocol RenderTarget: AnyObject { @@ -60,19 +57,32 @@ protocol RenderTarget: AnyObject { func redraw() } -final class MediaEditorRenderer: TextureConsumer { - var textureSource: TextureSource? { - didSet { - self.textureSource?.connect(to: self) +final class MediaEditorRenderer { + enum Input { + case texture(MTLTexture, CMTime) + case videoBuffer(VideoPixelBuffer) + + var timestamp: CMTime { + switch self { + case let .texture(_, timestamp): + return timestamp + case let .videoBuffer(videoBuffer): + return videoBuffer.timestamp + } } } + private var device: MTLDevice? + private var library: MTLLibrary? + private var commandQueue: MTLCommandQueue? + private var textureCache: CVMetalTextureCache? private var semaphore = DispatchSemaphore(value: 3) + private var renderPasses: [RenderPass] = [] - private let videoInputPass = VideoInputPass() + private let mainVideoInputPass = VideoInputPass() private let additionalVideoInputPass = VideoInputPass() - let videoFinishPass = VideoInputScalePass() + let videoFinishPass = VideoFinishPass() private let outputRenderPass = OutputRenderPass() private weak var renderTarget: RenderTarget? { @@ -81,22 +91,23 @@ final class MediaEditorRenderer: TextureConsumer { } } - private var device: MTLDevice? - private var library: MTLLibrary? - private var commandQueue: MTLCommandQueue? - private var textureCache: CVMetalTextureCache? + var textureSource: TextureSource? { + didSet { + self.textureSource?.connect(to: self) + } + } - private var currentTexture: MTLTexture? - private var currentAdditionalTexture: MTLTexture? - private var currentTime: CMTime = .zero + private var currentMainInput: Input? + private var currentAdditionalInput: Input? + private(set) var resultTexture: MTLTexture? - private var currentPixelBuffer: VideoPixelBuffer? - private var currentAdditionalPixelBuffer: VideoPixelBuffer? + var displayEnabled = true + var skipEditingPasses = false + var needsDisplay = false - public var onNextRender: (() -> Void)? + var onNextRender: (() -> Void)? + var onNextAdditionalRender: (() -> Void)? - var finalTexture: MTLTexture? - public init() { } @@ -120,11 +131,7 @@ final class MediaEditorRenderer: TextureConsumer { } } - private func setup() { - guard let device = self.renderTarget?.mtlDevice else { - return - } - + private func commonSetup(device: MTLDevice) { CVMetalTextureCacheCreate(nil, nil, device, nil, &self.textureCache) let mainBundle = Bundle(for: MediaEditorRenderer.self) @@ -142,10 +149,21 @@ final class MediaEditorRenderer: TextureConsumer { self.commandQueue = device.makeCommandQueue() self.commandQueue?.label = "Media Editor Command Queue" - self.videoInputPass.setup(device: device, library: library) + self.mainVideoInputPass.setup(device: device, library: library) self.additionalVideoInputPass.setup(device: device, library: library) self.videoFinishPass.setup(device: device, library: library) self.renderPasses.forEach { $0.setup(device: device, library: library) } + } + + private func setup() { + guard let device = self.renderTarget?.mtlDevice else { + return + } + + self.commonSetup(device: device) + guard let library = self.library else { + return + } self.outputRenderPass.setup(device: device, library: library) } @@ -154,33 +172,42 @@ final class MediaEditorRenderer: TextureConsumer { return } self.device = device - CVMetalTextureCacheCreate(nil, nil, device, nil, &self.textureCache) + self.commonSetup(device: device) + } + + func setRate(_ rate: Float) { + self.textureSource?.setRate(rate) + } - let mainBundle = Bundle(for: MediaEditorRenderer.self) - guard let path = mainBundle.path(forResource: "MediaEditorBundle", ofType: "bundle") else { - return + private func combinedTextureFromCurrentInputs(device: MTLDevice, commandBuffer: MTLCommandBuffer, textureCache: CVMetalTextureCache) -> MTLTexture? { + var mainTexture: MTLTexture? + var additionalTexture: MTLTexture? + + func textureFromInput(_ input: MediaEditorRenderer.Input, videoInputPass: VideoInputPass) -> MTLTexture? { + switch input { + case let .texture(texture, _): + return texture + case let .videoBuffer(videoBuffer): + return videoInputPass.processPixelBuffer(videoBuffer, textureCache: textureCache, device: device, commandBuffer: commandBuffer) + } } - guard let bundle = Bundle(path: path) else { - return + + guard let mainInput = self.currentMainInput else { + return nil } - guard let library = try? device.makeDefaultLibrary(bundle: bundle) else { - return + mainTexture = textureFromInput(mainInput, videoInputPass: self.mainVideoInputPass) + if let additionalInput = self.currentAdditionalInput { + additionalTexture = textureFromInput(additionalInput, videoInputPass: self.additionalVideoInputPass) } - self.library = library - self.commandQueue = device.makeCommandQueue() - self.commandQueue?.label = "Media Editor Command Queue" - self.videoInputPass.setup(device: device, library: library) - self.additionalVideoInputPass.setup(device: device, library: library) - self.videoFinishPass.setup(device: device, library: library) - self.renderPasses.forEach { $0.setup(device: device, library: library) } + if let mainTexture { + return self.videoFinishPass.process(input: mainTexture, secondInput: additionalTexture, timestamp: mainInput.timestamp, device: device, commandBuffer: commandBuffer) + } else { + return nil + } } - public var displayEnabled = true - var renderPassedEnabled = true - var needsDisplay = false - func renderFrame() { let device: MTLDevice? if let renderTarget = self.renderTarget { @@ -192,52 +219,22 @@ final class MediaEditorRenderer: TextureConsumer { } guard let device = device, let commandQueue = self.commandQueue, - let textureCache = self.textureCache else { + let textureCache = self.textureCache, + let commandBuffer = commandQueue.makeCommandBuffer(), + var texture = self.combinedTextureFromCurrentInputs(device: device, commandBuffer: commandBuffer, textureCache: textureCache) + else { self.didRenderFrame() return } - guard let commandBuffer = commandQueue.makeCommandBuffer() else { - self.didRenderFrame() - return - } - - var texture: MTLTexture - if let currentAdditionalTexture = self.currentAdditionalTexture, let currentTexture = self.currentTexture { - self.videoFinishPass.mainTextureRotation = .rotate0Degrees - self.videoFinishPass.additionalTextureRotation = .rotate0DegreesMirrored - if let result = self.videoFinishPass.process(input: currentTexture, secondInput: currentAdditionalTexture, timestamp: self.currentTime, device: device, commandBuffer: commandBuffer) { - texture = result - } else { - texture = currentTexture - } - } else if let currentTexture = self.currentTexture { - texture = currentTexture - } else if let currentPixelBuffer = self.currentPixelBuffer, let currentAdditionalPixelBuffer = self.currentAdditionalPixelBuffer, let videoTexture = self.videoInputPass.processPixelBuffer(currentPixelBuffer, textureCache: textureCache, device: device, commandBuffer: commandBuffer), let additionalVideoTexture = self.additionalVideoInputPass.processPixelBuffer(currentAdditionalPixelBuffer, textureCache: textureCache, device: device, commandBuffer: commandBuffer) { - if let result = self.videoFinishPass.process(input: videoTexture, secondInput: additionalVideoTexture, timestamp: currentPixelBuffer.timestamp, device: device, commandBuffer: commandBuffer) { - texture = result - } else { - texture = videoTexture - } - } else if let currentPixelBuffer = self.currentPixelBuffer, let videoTexture = self.videoInputPass.processPixelBuffer(currentPixelBuffer, textureCache: textureCache, device: device, commandBuffer: commandBuffer) { - if let result = self.videoFinishPass.process(input: videoTexture, secondInput: nil, timestamp: currentPixelBuffer.timestamp, device: device, commandBuffer: commandBuffer) { - texture = result - } else { - texture = videoTexture - } - } else { - self.didRenderFrame() - return - } - - if self.renderPassedEnabled { + if !self.skipEditingPasses { for renderPass in self.renderPasses { if let nextTexture = renderPass.process(input: texture, device: device, commandBuffer: commandBuffer) { texture = nextTexture } } } - self.finalTexture = texture + self.resultTexture = texture if self.renderTarget == nil { commandBuffer.addCompletedHandler { [weak self] _ in @@ -265,7 +262,7 @@ final class MediaEditorRenderer: TextureConsumer { let device = renderTarget.mtlDevice, let commandQueue = self.commandQueue, let commandBuffer = commandQueue.makeCommandBuffer(), - let texture = self.finalTexture + let texture = self.resultTexture else { self.needsDisplay = false self.didRenderFrame() @@ -281,6 +278,15 @@ final class MediaEditorRenderer: TextureConsumer { onNextRender() } } + + if let onNextAdditionalRender = self.onNextAdditionalRender { + if self.currentAdditionalInput != nil { + self.onNextAdditionalRender = nil + Queue.mainQueue().after(0.016) { + onNextAdditionalRender() + } + } + } } } @@ -299,49 +305,24 @@ final class MediaEditorRenderer: TextureConsumer { self.semaphore.signal() } - func consumeTexture(_ texture: MTLTexture, render: Bool) { - if render { - self.willRenderFrame() - } - - self.currentTexture = texture - if render { - self.renderFrame() - } - } - - func consumeTexture(_ texture: MTLTexture, additionalTexture: MTLTexture?, time: CMTime, render: Bool) { - self.displayEnabled = false + func consume( + main: MediaEditorRenderer.Input, + additional: MediaEditorRenderer.Input?, + render: Bool, + displayEnabled: Bool = true + ) { + self.displayEnabled = displayEnabled if render { self.willRenderFrame() } - self.currentTexture = texture - self.currentAdditionalTexture = additionalTexture - self.currentTime = time - if render { - self.renderFrame() - } - } - - var previousPresentationTimestamp: CMTime? - func consumeVideoPixelBuffer(pixelBuffer: VideoPixelBuffer, additionalPixelBuffer: VideoPixelBuffer?, render: Bool) { - self.willRenderFrame() + self.currentMainInput = main + self.currentAdditionalInput = additional - self.currentPixelBuffer = pixelBuffer - if additionalPixelBuffer == nil && self.currentAdditionalPixelBuffer != nil { - } else { - self.currentAdditionalPixelBuffer = additionalPixelBuffer - } if render { - if self.previousPresentationTimestamp == pixelBuffer.timestamp { - self.didRenderFrame() - } else { - self.renderFrame() - } + self.renderFrame() } - self.previousPresentationTimestamp = pixelBuffer.timestamp } func renderTargetDidChange(_ target: RenderTarget?) { @@ -354,7 +335,7 @@ final class MediaEditorRenderer: TextureConsumer { } func finalRenderedImage(mirror: Bool = false) -> UIImage? { - if let finalTexture = self.finalTexture, let device = self.renderTarget?.mtlDevice { + if let finalTexture = self.resultTexture, let device = self.renderTarget?.mtlDevice { return getTextureImage(device: device, texture: finalTexture, mirror: mirror) } else { return nil diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorUtils.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorUtils.swift index a93192fc585..33ef99a3909 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorUtils.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorUtils.swift @@ -1,4 +1,5 @@ import Foundation +import UIKit import AVFoundation import SwiftSignalKit @@ -39,3 +40,92 @@ extension AVPlayer { return timer } } + +func textureRotatonForAVAsset(_ asset: AVAsset, mirror: Bool = false) -> TextureRotation { + for track in asset.tracks { + if track.mediaType == .video { + let t = track.preferredTransform + if t.a == -1.0 && t.d == -1.0 { + return .rotate180Degrees + } else if t.a == 1.0 && t.d == 1.0 { + return .rotate0Degrees + } else if t.b == -1.0 && t.c == 1.0 { + return .rotate270Degrees + } else if t.a == -1.0 && t.d == 1.0 { + return .rotate270Degrees + } else if t.a == 1.0 && t.d == -1.0 { + return .rotate180Degrees + } else { + return mirror ? .rotate90DegreesMirrored : .rotate90Degrees + } + } + } + return .rotate0Degrees +} + +func loadTexture(image: UIImage, device: MTLDevice) -> MTLTexture? { + func dataForImage(_ image: UIImage) -> UnsafeMutablePointer { + let imageRef = image.cgImage + let width = Int(image.size.width) + let height = Int(image.size.height) + let colorSpace = CGColorSpaceCreateDeviceRGB() + + let rawData = UnsafeMutablePointer.allocate(capacity: width * height * 4) + let bytePerPixel = 4 + let bytesPerRow = bytePerPixel * Int(width) + let bitsPerComponent = 8 + let bitmapInfo = CGBitmapInfo.byteOrder32Little.rawValue + CGImageAlphaInfo.premultipliedFirst.rawValue + let context = CGContext.init(data: rawData, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) + context?.draw(imageRef!, in: CGRect(x: 0, y: 0, width: width, height: height)) + + return rawData + } + + let width = Int(image.size.width * image.scale) + let height = Int(image.size.height * image.scale) + let bytePerPixel = 4 + let bytesPerRow = bytePerPixel * width + + var texture : MTLTexture? + let region = MTLRegionMake2D(0, 0, Int(width), Int(height)) + let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .bgra8Unorm, width: width, height: height, mipmapped: false) + texture = device.makeTexture(descriptor: textureDescriptor) + + let data = dataForImage(image) + texture?.replace(region: region, mipmapLevel: 0, withBytes: data, bytesPerRow: bytesPerRow) + + return texture +} + +func pixelBufferToMTLTexture(pixelBuffer: CVPixelBuffer, textureCache: CVMetalTextureCache) -> MTLTexture? { + let width = CVPixelBufferGetWidth(pixelBuffer) + let height = CVPixelBufferGetHeight(pixelBuffer) + + let format: MTLPixelFormat = .r8Unorm + var textureRef : CVMetalTexture? + let status = CVMetalTextureCacheCreateTextureFromImage(nil, textureCache, pixelBuffer, nil, format, width, height, 0, &textureRef) + if status == kCVReturnSuccess { + return CVMetalTextureGetTexture(textureRef!) + } + + return nil +} + +func getTextureImage(device: MTLDevice, texture: MTLTexture, mirror: Bool = false) -> UIImage? { + let colorSpace = CGColorSpaceCreateDeviceRGB() + let context = CIContext(mtlDevice: device, options: [:]) + guard var ciImage = CIImage(mtlTexture: texture, options: [.colorSpace: colorSpace]) else { + return nil + } + let transform: CGAffineTransform + if mirror { + transform = CGAffineTransform(-1.0, 0.0, 0.0, -1.0, ciImage.extent.width, ciImage.extent.height) + } else { + transform = CGAffineTransform(1.0, 0.0, 0.0, -1.0, 0.0, ciImage.extent.height) + } + ciImage = ciImage.transformed(by: transform) + guard let cgImage = context.createCGImage(ciImage, from: CGRect(origin: .zero, size: CGSize(width: ciImage.extent.width, height: ciImage.extent.height))) else { + return nil + } + return UIImage(cgImage: cgImage) +} diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift index c5fab71d320..6fa55dd0bbc 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift @@ -264,9 +264,15 @@ public final class MediaEditorValues: Codable, Equatable { if lhs.videoIsMirrored != rhs.videoIsMirrored { return false } + if lhs.videoVolume != rhs.videoVolume { + return false + } if lhs.additionalVideoPath != rhs.additionalVideoPath { return false } + if lhs.additionalVideoIsDual != rhs.additionalVideoIsDual { + return false + } if lhs.additionalVideoPosition != rhs.additionalVideoPosition { return false } @@ -279,6 +285,15 @@ public final class MediaEditorValues: Codable, Equatable { if lhs.additionalVideoPositionChanges != rhs.additionalVideoPositionChanges { return false } + if lhs.additionalVideoTrimRange != rhs.additionalVideoTrimRange { + return false + } + if lhs.additionalVideoOffset != rhs.additionalVideoOffset { + return false + } + if lhs.additionalVideoVolume != rhs.additionalVideoVolume { + return false + } if lhs.drawing !== rhs.drawing { return false } @@ -347,11 +362,17 @@ public final class MediaEditorValues: Codable, Equatable { case videoIsMuted case videoIsFullHd case videoIsMirrored + case videoVolume case additionalVideoPath + case additionalVideoIsDual case additionalVideoPosition case additionalVideoScale case additionalVideoRotation case additionalVideoPositionChanges + case additionalVideoTrimRange + case additionalVideoOffset + case additionalVideoVolume + case drawing case entities case toolValues @@ -378,13 +399,19 @@ public final class MediaEditorValues: Codable, Equatable { public let videoIsMuted: Bool public let videoIsFullHd: Bool public let videoIsMirrored: Bool + public let videoVolume: CGFloat? public let additionalVideoPath: String? + public let additionalVideoIsDual: Bool public let additionalVideoPosition: CGPoint? public let additionalVideoScale: CGFloat? public let additionalVideoRotation: CGFloat? public let additionalVideoPositionChanges: [VideoPositionChange] + public let additionalVideoTrimRange: Range? + public let additionalVideoOffset: Double? + public let additionalVideoVolume: CGFloat? + public let drawing: UIImage? public let entities: [CodableDrawingEntity] public let toolValues: [EditorToolKey: Any] @@ -415,11 +442,16 @@ public final class MediaEditorValues: Codable, Equatable { videoIsMuted: Bool, videoIsFullHd: Bool, videoIsMirrored: Bool, + videoVolume: CGFloat?, additionalVideoPath: String?, + additionalVideoIsDual: Bool, additionalVideoPosition: CGPoint?, additionalVideoScale: CGFloat?, additionalVideoRotation: CGFloat?, additionalVideoPositionChanges: [VideoPositionChange], + additionalVideoTrimRange: Range?, + additionalVideoOffset: Double?, + additionalVideoVolume: CGFloat?, drawing: UIImage?, entities: [CodableDrawingEntity], toolValues: [EditorToolKey: Any], @@ -443,11 +475,16 @@ public final class MediaEditorValues: Codable, Equatable { self.videoIsMuted = videoIsMuted self.videoIsFullHd = videoIsFullHd self.videoIsMirrored = videoIsMirrored + self.videoVolume = videoVolume self.additionalVideoPath = additionalVideoPath + self.additionalVideoIsDual = additionalVideoIsDual self.additionalVideoPosition = additionalVideoPosition self.additionalVideoScale = additionalVideoScale self.additionalVideoRotation = additionalVideoRotation self.additionalVideoPositionChanges = additionalVideoPositionChanges + self.additionalVideoTrimRange = additionalVideoTrimRange + self.additionalVideoOffset = additionalVideoOffset + self.additionalVideoVolume = additionalVideoVolume self.drawing = drawing self.entities = entities self.toolValues = toolValues @@ -485,12 +522,17 @@ public final class MediaEditorValues: Codable, Equatable { self.videoIsMuted = try container.decode(Bool.self, forKey: .videoIsMuted) self.videoIsFullHd = try container.decodeIfPresent(Bool.self, forKey: .videoIsFullHd) ?? false self.videoIsMirrored = try container.decodeIfPresent(Bool.self, forKey: .videoIsMirrored) ?? false + self.videoVolume = try container.decodeIfPresent(CGFloat.self, forKey: .videoVolume) ?? 1.0 self.additionalVideoPath = try container.decodeIfPresent(String.self, forKey: .additionalVideoPath) + self.additionalVideoIsDual = try container.decodeIfPresent(Bool.self, forKey: .additionalVideoIsDual) ?? false self.additionalVideoPosition = try container.decodeIfPresent(CGPoint.self, forKey: .additionalVideoPosition) self.additionalVideoScale = try container.decodeIfPresent(CGFloat.self, forKey: .additionalVideoScale) self.additionalVideoRotation = try container.decodeIfPresent(CGFloat.self, forKey: .additionalVideoRotation) self.additionalVideoPositionChanges = try container.decodeIfPresent([VideoPositionChange].self, forKey: .additionalVideoPositionChanges) ?? [] + self.additionalVideoTrimRange = try container.decodeIfPresent(Range.self, forKey: .additionalVideoTrimRange) + self.additionalVideoOffset = try container.decodeIfPresent(Double.self, forKey: .additionalVideoOffset) + self.additionalVideoVolume = try container.decodeIfPresent(CGFloat.self, forKey: .additionalVideoVolume) if let drawingData = try container.decodeIfPresent(Data.self, forKey: .drawing), let image = UIImage(data: drawingData) { self.drawing = image @@ -541,12 +583,17 @@ public final class MediaEditorValues: Codable, Equatable { try container.encode(self.videoIsMuted, forKey: .videoIsMuted) try container.encode(self.videoIsFullHd, forKey: .videoIsFullHd) try container.encode(self.videoIsMirrored, forKey: .videoIsMirrored) + try container.encode(self.videoVolume, forKey: .videoVolume) try container.encodeIfPresent(self.additionalVideoPath, forKey: .additionalVideoPath) + try container.encodeIfPresent(self.additionalVideoIsDual, forKey: .additionalVideoIsDual) try container.encodeIfPresent(self.additionalVideoPosition, forKey: .additionalVideoPosition) try container.encodeIfPresent(self.additionalVideoScale, forKey: .additionalVideoScale) try container.encodeIfPresent(self.additionalVideoRotation, forKey: .additionalVideoRotation) try container.encodeIfPresent(self.additionalVideoPositionChanges, forKey: .additionalVideoPositionChanges) + try container.encodeIfPresent(self.additionalVideoTrimRange, forKey: .additionalVideoTrimRange) + try container.encodeIfPresent(self.additionalVideoOffset, forKey: .additionalVideoOffset) + try container.encodeIfPresent(self.additionalVideoVolume, forKey: .additionalVideoVolume) if let drawing = self.drawing, let pngDrawingData = drawing.pngData() { try container.encode(pngDrawingData, forKey: .drawing) @@ -571,68 +618,85 @@ public final class MediaEditorValues: Codable, Equatable { } public func makeCopy() -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedCrop(offset: CGPoint, scale: CGFloat, rotation: CGFloat, mirroring: Bool) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: offset, cropRect: self.cropRect, cropScale: scale, cropRotation: rotation, cropMirroring: mirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: offset, cropRect: self.cropRect, cropScale: scale, cropRotation: rotation, cropMirroring: mirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedGradientColors(gradientColors: [UIColor]) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedVideoIsMuted(_ videoIsMuted: Bool) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedVideoIsFullHd(_ videoIsFullHd: Bool) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedVideoIsMirrored(_ videoIsMirrored: Bool) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + } + + func withUpdatedVideoVolume(_ videoVolume: CGFloat?) -> MediaEditorValues { + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } - func withUpdatedAdditionalVideo(path: String, positionChanges: [VideoPositionChange]) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: path, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: positionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + func withUpdatedAdditionalVideo(path: String?, isDual: Bool, positionChanges: [VideoPositionChange]) -> MediaEditorValues { + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: path, additionalVideoIsDual: isDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: positionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedAdditionalVideo(position: CGPoint, scale: CGFloat, rotation: CGFloat) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: position, additionalVideoScale: scale, additionalVideoRotation: rotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: position, additionalVideoScale: scale, additionalVideoRotation: rotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + } + + func withUpdatedAdditionalVideoTrimRange(_ additionalVideoTrimRange: Range?) -> MediaEditorValues { + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + } + + + func withUpdatedAdditionalVideoOffset(_ additionalVideoOffset: Double?) -> MediaEditorValues { + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + } + + func withUpdatedAdditionalVideoVolume(_ additionalVideoVolume: CGFloat?) -> MediaEditorValues { + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedVideoTrimRange(_ videoTrimRange: Range) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedDrawingAndEntities(drawing: UIImage?, entities: [CodableDrawingEntity]) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: drawing, entities: entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: drawing, entities: entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedToolValues(_ toolValues: [EditorToolKey: Any]) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedAudioTrack(_ audioTrack: MediaAudioTrack?) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedAudioTrackTrimRange(_ audioTrackTrimRange: Range?) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedAudioTrackOffset(_ audioTrackOffset: Double?) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedAudioTrackVolume(_ audioTrackVolume: CGFloat?) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedAudioTrackSamples(_ audioTrackSamples: MediaAudioTrackSamples?) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: audioTrackSamples, qualityPreset: self.qualityPreset) } public var resultDimensions: PixelDimensions { diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift index 91c821b9021..232a4815384 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift @@ -232,6 +232,24 @@ public final class MediaEditorVideoExport { } } + var additionalVideoTimeRange: CMTimeRange? { + if let videoTrimRange = self.values.additionalVideoTrimRange { + return CMTimeRange(start: CMTime(seconds: videoTrimRange.lowerBound, preferredTimescale: CMTimeScale(NSEC_PER_SEC)), end: CMTime(seconds: videoTrimRange.upperBound, preferredTimescale: CMTimeScale(NSEC_PER_SEC))) + } else { + return nil + } + } + + var additionalVideoStartTime: CMTime { + let lowerBound = self.values.additionalVideoTrimRange?.lowerBound ?? 0.0 + let offset = -min(0.0, self.values.additionalVideoOffset ?? 0.0) + if !lowerBound.isZero || !offset.isZero { + return CMTime(seconds: offset + lowerBound, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) + } else { + return .zero + } + } + var audioTimeRange: CMTimeRange? { if let audioTrack = self.values.audioTrack { let offset = max(0.0, self.values.audioTrackOffset ?? 0.0) @@ -304,6 +322,7 @@ public final class MediaEditorVideoExport { public private(set) var internalStatus: Status = .idle + private let queue = Queue() private let postbox: Postbox private let subject: Subject private let configuration: Configuration @@ -311,21 +330,22 @@ public final class MediaEditorVideoExport { private let outputPath: String private var reader: AVAssetReader? - private var additionalReader: AVAssetReader? - private var videoOutput: AVAssetReaderOutput? - private var audioOutput: AVAssetReaderOutput? private var textureRotation: TextureRotation = .rotate0Degrees + private var frameRate: Float? private var additionalVideoOutput: AVAssetReaderOutput? private var additionalTextureRotation: TextureRotation = .rotate0Degrees + private var additionalFrameRate: Float? + private var additionalVideoDuration: Double? - private let queue = Queue() + private var mainComposeFramerate: Float? + private var audioOutput: AVAssetReaderOutput? + private var writer: MediaEditorVideoExportWriter? private var composer: MediaEditorComposer? - private let duration = ValuePromise() private var durationValue: CMTime? { didSet { @@ -335,6 +355,8 @@ public final class MediaEditorVideoExport { } } + private var imageArguments: (frameRate: Double, position: CMTime)? + private let pauseDispatchGroup = DispatchGroup() private var cancelled = false @@ -352,7 +374,6 @@ public final class MediaEditorVideoExport { if FileManager.default.fileExists(atPath: outputPath) { try? FileManager.default.removeItem(atPath: outputPath) } - self.setup() let _ = NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: nil, using: { [weak self] _ in @@ -369,117 +390,221 @@ public final class MediaEditorVideoExport { }) } + enum Input { + case image(UIImage) + case video(AVAsset) + + var isVideo: Bool { + if case .video = self { + return true + } + return false + } + } + private func setup() { - if case let .video(asset, isStory) = self.subject { - if let trimmedVideoDuration = self.configuration.timeRange?.duration { - self.durationValue = trimmedVideoDuration + var mainAsset: AVAsset? + + var additionalAsset: AVAsset? + if let additionalPath = self.configuration.values.additionalVideoPath { + additionalAsset = AVURLAsset(url: URL(fileURLWithPath: additionalPath)) + } + + var audioAsset: AVAsset? + if let audioTrack = self.configuration.values.audioTrack { + let audioPath = fullDraftPath(peerId: self.configuration.values.peerId, path: audioTrack.path) + audioAsset = AVURLAsset(url: URL(fileURLWithPath: audioPath)) + } + + var mainInput: Input + let additionalInput: Input? = additionalAsset.flatMap { .video($0) } + var isStory = true + + switch self.subject { + case let .video(asset, isStoryValue): + mainAsset = asset + mainInput = .video(asset) + isStory = isStoryValue + case let .image(image): + mainInput = .image(image) + } + + let duration: CMTime + if let mainAsset { + if let trimmedDuration = self.configuration.timeRange?.duration { + duration = trimmedDuration } else { - asset.loadValuesAsynchronously(forKeys: ["tracks", "duration"]) { - if asset.duration.seconds > 60.0 && isStory { - self.durationValue = CMTime(seconds: 60.0, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) - } else { - self.durationValue = asset.duration - } + if isStory && mainAsset.duration.seconds > 60.0 { + duration = CMTime(seconds: 60.0, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) + } else { + duration = mainAsset.duration + } + } + } else if let additionalAsset { + if let trimmedDuration = self.configuration.additionalVideoTimeRange?.duration { + duration = trimmedDuration + } else { + if additionalAsset.duration.seconds > 60.0 { + duration = CMTime(seconds: 60.0, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) + } else { + duration = additionalAsset.duration } } } else { - self.durationValue = CMTime(seconds: 5, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) - } - - switch self.subject { - case let .video(asset, isStory): - var additionalAsset: AVAsset? - if let additionalPath = self.configuration.values.additionalVideoPath { - additionalAsset = AVURLAsset(url: URL(fileURLWithPath: additionalPath)) + if let audioDuration = self.configuration.audioTimeRange?.duration { + duration = audioDuration + } else { + duration = CMTime(seconds: 5.0, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) } - self.setupWithAsset(asset, additionalAsset: additionalAsset, isStory: isStory) - case let .image(image): - self.setupWithImage(image) } + self.durationValue = duration + + self.setupWithInputs(main: mainInput, additional: additionalInput, audio: audioAsset, isStory: isStory) } private func setupComposer() { guard self.composer == nil else { return } - self.composer = MediaEditorComposer(postbox: self.postbox, values: self.configuration.values, dimensions: self.configuration.composerDimensions, outputDimensions: self.configuration.dimensions, textScale: self.textScale) + + var duration = self.durationValue?.seconds + if case .image = self.subject { + duration = nil + } + + self.composer = MediaEditorComposer( + postbox: self.postbox, + values: self.configuration.values, + dimensions: self.configuration.composerDimensions, + outputDimensions: self.configuration.dimensions, + textScale: self.textScale, + videoDuration: duration, + additionalVideoDuration: self.additionalVideoDuration + ) } - private func setupWithAsset(_ asset: AVAsset, additionalAsset: AVAsset?, isStory: Bool) { - var inputAsset = asset + private func setupWithInputs(main: Input, additional: Input?, audio: AVAsset?, isStory: Bool) { + var hasVideoOrAudio = false + if main.isVideo || additional?.isVideo == true || audio != nil { + hasVideoOrAudio = true + } + + var composition: AVMutableComposition? + var mainVideoTrack: AVMutableCompositionTrack? + var additionalVideoTrack: AVMutableCompositionTrack? + var audioMix: AVMutableAudioMix? - var inputAudioMix: AVMutableAudioMix? - if let audioData = self.configuration.values.audioTrack { - let mixComposition = AVMutableComposition() - let audioPath = fullDraftPath(peerId: self.configuration.values.peerId, path: audioData.path) - let audioAsset = AVURLAsset(url: URL(fileURLWithPath: audioPath)) + if hasVideoOrAudio, let duration = self.durationValue { + composition = AVMutableComposition() + var audioMixParameters: [AVMutableAudioMixInputParameters] = [] - guard - let videoTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid), - let musicTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid), - let videoAssetTrack = asset.tracks(withMediaType: .video).first, - let musicAssetTrack = audioAsset.tracks(withMediaType: .audio).first, - let duration = self.durationValue - else { - print("error") - return + let wholeRange: CMTimeRange = CMTimeRangeMake(start: .zero, duration: duration) + func clampedRange(trackDuration: CMTime, trackTrimRange: CMTimeRange?, trackStart: CMTime, maxDuration: CMTime) -> CMTimeRange { + var result = CMTimeRange(start: .zero, duration: trackDuration) + if let trackTrimRange { + result = trackTrimRange + } + if trackStart + result.duration > maxDuration { + result = CMTimeRange(start: result.start, end: maxDuration - trackStart) + } + return result } - videoTrack.preferredTransform = videoAssetTrack.preferredTransform - let timeRange: CMTimeRange = CMTimeRangeMake(start: .zero, duration: duration) - try? videoTrack.insertTimeRange(timeRange, of: videoAssetTrack, at: .zero) - - if let audioAssetTrack = asset.tracks(withMediaType: .audio).first, let audioTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid), !self.configuration.values.videoIsMuted { - try? audioTrack.insertTimeRange(timeRange, of: audioAssetTrack, at: .zero) + var readerRange = wholeRange + if case let .video(asset) = main { + self.textureRotation = textureRotatonForAVAsset(asset) + if let videoAssetTrack = asset.tracks(withMediaType: .video).first { + if let compositionTrack = composition?.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid) { + mainVideoTrack = compositionTrack + compositionTrack.preferredTransform = videoAssetTrack.preferredTransform + + try? compositionTrack.insertTimeRange(CMTimeRange(start: .zero, duration: asset.duration), of: videoAssetTrack, at: .zero) + } + } + if let audioAssetTrack = asset.tracks(withMediaType: .audio).first, !self.configuration.values.videoIsMuted { + if let compositionTrack = composition?.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) { + try? compositionTrack.insertTimeRange(CMTimeRange(start: .zero, duration: asset.duration), of: audioAssetTrack, at: .zero) + + if let volume = self.configuration.values.videoVolume, volume != 1.0 { + let trackParameters = AVMutableAudioMixInputParameters(track: compositionTrack) + trackParameters.trackID = compositionTrack.trackID + trackParameters.setVolume(Float(volume), at: .zero) + audioMixParameters.append(trackParameters) + } + } + } + if let timeRange = self.configuration.timeRange { + readerRange = timeRange + } } - - var musicRange = timeRange - let musicStartTime = self.configuration.audioStartTime - if let audioTrackRange = self.configuration.audioTimeRange { - musicRange = audioTrackRange + if let additional, case let .video(asset) = additional { + self.additionalTextureRotation = textureRotatonForAVAsset(asset, mirror: true) + self.additionalVideoDuration = asset.duration.seconds + + let startTime: CMTime + let timeRange: CMTimeRange + if mainVideoTrack == nil { + startTime = .zero + timeRange = CMTimeRange(start: .zero, end: asset.duration) + } else { + startTime = self.configuration.additionalVideoStartTime + timeRange = clampedRange(trackDuration: asset.duration, trackTrimRange: self.configuration.additionalVideoTimeRange, trackStart: startTime, maxDuration: readerRange.end) + } + + if let videoAssetTrack = asset.tracks(withMediaType: .video).first { + if let compositionTrack = composition?.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid) { + additionalVideoTrack = compositionTrack + compositionTrack.preferredTransform = videoAssetTrack.preferredTransform + + try? compositionTrack.insertTimeRange(timeRange, of: videoAssetTrack, at: startTime) + } + } + if let audioAssetTrack = asset.tracks(withMediaType: .audio).first, self.configuration.values.additionalVideoVolume ?? 1.0 > 0.01 { + if let compositionTrack = composition?.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) { + try? compositionTrack.insertTimeRange(timeRange, of: audioAssetTrack, at: startTime) + + if let volume = self.configuration.values.additionalVideoVolume, volume != 1.0 { + let trackParameters = AVMutableAudioMixInputParameters(track: compositionTrack) + trackParameters.trackID = compositionTrack.trackID + trackParameters.setVolume(Float(volume), at: .zero) + audioMixParameters.append(trackParameters) + } + } + } + if mainVideoTrack == nil, let timeRange = self.configuration.additionalVideoTimeRange { + readerRange = timeRange + } } - if musicStartTime + musicRange.duration > duration { - musicRange = CMTimeRange(start: musicRange.start, end: duration - musicStartTime) + if let audio, let audioAssetTrack = audio.tracks(withMediaType: .audio).first { + let startTime: CMTime + if mainVideoTrack == nil && additionalVideoTrack == nil { + startTime = .zero + } else { + startTime = self.configuration.audioStartTime + } + let timeRange = clampedRange(trackDuration: audio.duration, trackTrimRange: self.configuration.audioTimeRange, trackStart: startTime, maxDuration: readerRange.end) + + if let compositionTrack = composition?.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) { + try? compositionTrack.insertTimeRange(timeRange, of: audioAssetTrack, at: startTime) + + if let volume = self.configuration.values.audioTrackVolume, volume != 1.0 { + let trackParameters = AVMutableAudioMixInputParameters(track: compositionTrack) + trackParameters.trackID = compositionTrack.trackID + trackParameters.setVolume(Float(volume), at: .zero) + audioMixParameters.append(trackParameters) + } + } } - try? musicTrack.insertTimeRange(musicRange, of: musicAssetTrack, at: musicStartTime) - if let volume = self.configuration.values.audioTrackVolume, volume < 1.0 { - let audioMix = AVMutableAudioMix() - var audioMixParam: [AVMutableAudioMixInputParameters] = [] - let param: AVMutableAudioMixInputParameters = AVMutableAudioMixInputParameters(track: musicTrack) - param.trackID = musicTrack.trackID - param.setVolume(Float(volume), at: CMTime.zero) - audioMixParam.append(param) - audioMix.inputParameters = audioMixParam - inputAudioMix = audioMix + if !audioMixParameters.isEmpty { + audioMix = AVMutableAudioMix() + audioMix?.inputParameters = audioMixParameters } - inputAsset = mixComposition - } - - self.reader = try? AVAssetReader(asset: inputAsset) - - var mirror = false - if additionalAsset == nil, self.configuration.values.videoIsMirrored { - mirror = true - } - - self.textureRotation = textureRotatonForAVAsset(asset, mirror: mirror) - - if let additionalAsset { - self.additionalReader = try? AVAssetReader(asset: additionalAsset) - self.additionalTextureRotation = textureRotatonForAVAsset(additionalAsset, mirror: true) - } - guard let reader = self.reader else { - return - } - if let timeRange = self.configuration.timeRange { - reader.timeRange = timeRange - self.additionalReader?.timeRange = timeRange - } else if asset.duration.seconds > 60.0 && isStory { - let trimmedRange = CMTimeRange(start: CMTime(seconds: 0.0, preferredTimescale: CMTimeScale(NSEC_PER_SEC)), end: CMTime(seconds: 60.0, preferredTimescale: CMTimeScale(NSEC_PER_SEC))) - reader.timeRange = trimmedRange - self.additionalReader?.timeRange = trimmedRange + if let composition { + self.reader = try? AVAssetReader(asset: composition) + self.reader?.timeRange = readerRange + } } self.writer = MediaEditorVideoAVAssetWriter() @@ -487,360 +612,237 @@ public final class MediaEditorVideoExport { return } writer.setup(configuration: self.configuration, outputPath: self.outputPath) + self.setupComposer() - let videoTracks = inputAsset.tracks(withMediaType: .video) - let additionalVideoTracks = additionalAsset?.tracks(withMediaType: .video) - if videoTracks.count > 0 { - var sourceFrameRate: Float = 0.0 + if let reader { let colorProperties: [String: Any] = [ AVVideoColorPrimariesKey: AVVideoColorPrimaries_ITU_R_709_2, AVVideoTransferFunctionKey: AVVideoTransferFunction_ITU_R_709_2, AVVideoYCbCrMatrixKey: AVVideoYCbCrMatrix_ITU_R_709_2 ] - let outputSettings: [String: Any] = [ kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, kCVPixelBufferMetalCompatibilityKey as String: true, AVVideoColorPropertiesKey: colorProperties ] - - let originalDimensions = self.configuration.values.originalDimensions - var isNotFullscreen = false - var hasNonIdentityTransform = false - if case .video(_, true) = self.subject { - if originalDimensions.width > 0 && abs((Double(originalDimensions.height) / Double(originalDimensions.width)) - 1.7777778) > 0.001 { - isNotFullscreen = true - } - if let videoTrack = videoTracks.first { - hasNonIdentityTransform = !videoTrack.preferredTransform.isIdentity - } - } - var preferredTransform: CGAffineTransform? - if let videoTrack = videoTracks.first, !self.configuration.values.requiresComposing && !isNotFullscreen && !hasNonIdentityTransform { - preferredTransform = videoTrack.preferredTransform - } else { - self.setupComposer() - } - let videoOutput = AVAssetReaderTrackOutput(track: videoTracks.first!, outputSettings: outputSettings) - videoOutput.alwaysCopiesSampleData = true - if reader.canAdd(videoOutput) { - reader.add(videoOutput) - } else { - self.internalStatus = .finished - self.statusValue = .failed(.addVideoOutput) - } - self.videoOutput = videoOutput - - if let additionalReader = self.additionalReader, let additionalVideoTrack = additionalVideoTracks?.first { - let additionalVideoOutput = AVAssetReaderTrackOutput(track: additionalVideoTrack, outputSettings: outputSettings) - additionalVideoOutput.alwaysCopiesSampleData = true - if additionalReader.canAdd(additionalVideoOutput) { - additionalReader.add(additionalVideoOutput) + if let mainVideoTrack { + let videoOutput = AVAssetReaderTrackOutput(track: mainVideoTrack, outputSettings: outputSettings) + videoOutput.alwaysCopiesSampleData = true + if reader.canAdd(videoOutput) { + reader.add(videoOutput) + } else { + self.internalStatus = .finished + self.statusValue = .failed(.addVideoOutput) } - self.additionalVideoOutput = additionalVideoOutput + self.videoOutput = videoOutput } - - if let videoTrack = videoTracks.first { - if videoTrack.nominalFrameRate > 0.0 { - sourceFrameRate = videoTrack.nominalFrameRate - } else if videoTrack.minFrameDuration.seconds > 0.0 { - sourceFrameRate = Float(1.0 / videoTrack.minFrameDuration.seconds) + if let additionalVideoTrack { + let videoOutput = AVAssetReaderTrackOutput(track: additionalVideoTrack, outputSettings: outputSettings) + videoOutput.alwaysCopiesSampleData = true + if reader.canAdd(videoOutput) { + reader.add(videoOutput) } else { - sourceFrameRate = 30.0 + self.internalStatus = .finished + self.statusValue = .failed(.addVideoOutput) } - } else { - sourceFrameRate = 30.0 + self.additionalVideoOutput = videoOutput } - writer.setupVideoInput(configuration: self.configuration, preferredTransform: preferredTransform, sourceFrameRate: sourceFrameRate) - } else { - self.videoOutput = nil } - let audioTracks = inputAsset.tracks(withMediaType: .audio) - if audioTracks.count > 0, !self.configuration.values.videoIsMuted || self.configuration.values.audioTrack != nil { - let audioOutput = AVAssetReaderAudioMixOutput(audioTracks: audioTracks, audioSettings: nil) - audioOutput.audioMix = inputAudioMix - audioOutput.alwaysCopiesSampleData = false - if reader.canAdd(audioOutput) { - reader.add(audioOutput) - } else { - self.internalStatus = .finished - self.statusValue = .failed(.addAudioOutput) + func frameRate(for track: AVCompositionTrack) -> Float { + if track.nominalFrameRate > 0.0 { + return track.nominalFrameRate + } else if track.minFrameDuration.seconds > 0.0 { + return Float(1.0 / track.minFrameDuration.seconds) } - self.audioOutput = audioOutput - - writer.setupAudioInput(configuration: self.configuration) - } else { - self.audioOutput = nil + return 30.0 } - if videoTracks.count == 0 && audioTracks.count == 0 { - self.internalStatus = .finished - self.statusValue = .failed(.noTracksFound) + if let mainVideoTrack { + self.frameRate = frameRate(for: mainVideoTrack) } - } - - private func setupWithImage(_ image: UIImage) { - Logger.shared.log("VideoExport", "Setup with image") - - self.setupComposer() - - var inputAudioMix: AVMutableAudioMix? - - self.writer = MediaEditorVideoAVAssetWriter() - guard let writer = self.writer else { - return + if let additionalVideoTrack { + self.additionalFrameRate = frameRate(for: additionalVideoTrack) } - writer.setup(configuration: self.configuration, outputPath: self.outputPath) - writer.setupVideoInput(configuration: self.configuration, preferredTransform: nil, sourceFrameRate: 30.0) - - if let audioData = self.configuration.values.audioTrack { - let mixComposition = AVMutableComposition() - let audioPath = fullDraftPath(peerId: self.configuration.values.peerId, path: audioData.path) - let audioAsset = AVURLAsset(url: URL(fileURLWithPath: audioPath)) - - if let musicAssetTrack = audioAsset.tracks(withMediaType: .audio).first, - let musicTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) { - do { - let reader = try AVAssetReader(asset: mixComposition) - - var musicRange = CMTimeRange(start: .zero, duration: CMTime(seconds: min(15.0, audioData.duration), preferredTimescale: CMTimeScale(NSEC_PER_SEC))) - if let audioTrackRange = self.configuration.audioTimeRange { - musicRange = audioTrackRange - } - try? musicTrack.insertTimeRange(musicRange, of: musicAssetTrack, at: .zero) - - if let volume = self.configuration.values.audioTrackVolume, volume < 1.0 { - let audioMix = AVMutableAudioMix() - var audioMixParam: [AVMutableAudioMixInputParameters] = [] - let param: AVMutableAudioMixInputParameters = AVMutableAudioMixInputParameters(track: musicTrack) - param.trackID = musicTrack.trackID - param.setVolume(Float(volume), at: CMTime.zero) - audioMixParam.append(param) - audioMix.inputParameters = audioMixParam - inputAudioMix = audioMix - } - - let audioTracks = mixComposition.tracks(withMediaType: .audio) - let audioOutput = AVAssetReaderAudioMixOutput(audioTracks: audioTracks, audioSettings: nil) - audioOutput.audioMix = inputAudioMix - audioOutput.alwaysCopiesSampleData = false - if reader.canAdd(audioOutput) { - reader.add(audioOutput) - - self.reader = reader - self.audioOutput = audioOutput - - writer.setupAudioInput(configuration: self.configuration) - } else { - self.internalStatus = .finished - self.statusValue = .failed(.addAudioOutput) - } - } catch { + let sourceFrameRate: Float = (self.frameRate ?? self.additionalFrameRate) ?? 30.0 + self.mainComposeFramerate = round(sourceFrameRate / 30.0) * 30.0 + writer.setupVideoInput(configuration: self.configuration, preferredTransform: nil, sourceFrameRate: sourceFrameRate) + + if let reader { + let audioTracks = composition?.tracks(withMediaType: .audio) ?? [] + if audioTracks.count > 0 { + let audioOutput = AVAssetReaderAudioMixOutput(audioTracks: audioTracks, audioSettings: nil) + audioOutput.audioMix = audioMix + audioOutput.alwaysCopiesSampleData = false + if reader.canAdd(audioOutput) { + reader.add(audioOutput) + } else { self.internalStatus = .finished self.statusValue = .failed(.addAudioOutput) } + self.audioOutput = audioOutput + + writer.setupAudioInput(configuration: self.configuration) } } } - private func finish() { - assert(self.queue.isCurrent()) + private var skippingAdditionalCopyUpdate = false + private func encodeVideo() -> Bool { guard let writer = self.writer else { - return - } - - let outputUrl = URL(fileURLWithPath: self.outputPath) - - var cancelled = false - if let reader = self.reader, reader.status == .cancelled { - if writer.status != .cancelled { - writer.cancelWriting() - } - cancelled = true - } - - if writer.status == .cancelled { - if let reader = self.reader, reader.status != .cancelled { - reader.cancelReading() - } - cancelled = true - } - - if cancelled { - try? FileManager.default.removeItem(at: outputUrl) - self.internalStatus = .finished - self.statusValue = .failed(.cancelled) - return - } - - if writer.status == .failed { - if let error = writer.error { - Logger.shared.log("VideoExport", "Failed with writer error \(error.localizedDescription)") - } - try? FileManager.default.removeItem(at: outputUrl) - self.internalStatus = .finished - self.statusValue = .failed(.writing(nil)) - } else if let reader = self.reader, reader.status == .failed { - if let error = reader.error { - Logger.shared.log("VideoExport", "Failed with reader error \(error.localizedDescription)") - } - try? FileManager.default.removeItem(at: outputUrl) - writer.cancelWriting() - self.internalStatus = .finished - self.statusValue = .failed(.reading(reader.error)) - } else { - writer.finishWriting { - self.queue.async { - if writer.status == .failed { - if let error = writer.error { - Logger.shared.log("VideoExport", "Failed after finishWriting with writer error \(error.localizedDescription)") - } - try? FileManager.default.removeItem(at: outputUrl) - self.internalStatus = .finished - self.statusValue = .failed(.writing(nil)) - } else { - self.internalStatus = .finished - self.statusValue = .completed - - let end = CACurrentMediaTime() - let _ = (self.duration.get() - |> take(1)).start(next: { duration in - let exportDuration = end - self.startTimestamp - print("video processing took \(exportDuration)s") - if duration.seconds > 0 { - Logger.shared.log("VideoExport", "Video processing took \(exportDuration / duration.seconds)") - } - }) - } - } - } - } - } - - private var imageArguments: (duration: Double, frameRate: Double, position: CMTime)? - - private func encodeImageVideo() -> Bool { - guard let writer = self.writer, let composer = self.composer, case let .image(image) = self.subject, let imageArguments = self.imageArguments else { return false } - let duration = imageArguments.duration - let frameRate = imageArguments.frameRate - var position = imageArguments.position - var appendFailed = false while writer.isReadyForMoreVideoData { if appendFailed { return false } + + if let reader = self.reader, reader.status != .reading { + writer.markVideoAsFinished() + return false + } if writer.status != .writing { - Logger.shared.log("VideoExport", "Video finished") writer.markVideoAsFinished() return false } self.pauseDispatchGroup.wait() - let progress = (position - .zero).seconds / duration - self.statusValue = .progress(Float(progress)) - composer.processImage(inputImage: image, pool: writer.pixelBufferPool, time: position, completion: { pixelBuffer in - if let pixelBuffer { - if !writer.appendPixelBuffer(pixelBuffer, at: position) { - Logger.shared.log("VideoExport", "Failed to append pixelbuffer at \(position.seconds), stopping") - writer.markVideoAsFinished() - appendFailed = true - self.semaphore.signal() - } else { - Logger.shared.log("VideoExport", "Appended pixelbuffer at \(position.seconds)") - - Thread.sleep(forTimeInterval: 0.01) - self.semaphore.signal() + var updatedProgress = false + + var mainInput: MediaEditorComposer.Input? + var additionalInput: MediaEditorComposer.Input? + var mainTimestamp: CMTime? + if let videoOutput = self.videoOutput { + if let sampleBuffer = videoOutput.copyNextSampleBuffer() { + if let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) { + let timestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) + mainTimestamp = timestamp + mainInput = .videoBuffer(VideoPixelBuffer( + pixelBuffer: pixelBuffer, + rotation: self.textureRotation, + timestamp: timestamp + )) + + if let duration = self.durationValue { + let startTime = self.reader?.timeRange.start.seconds ?? 0.0 + let progress = (timestamp.seconds - startTime) / duration.seconds + self.statusValue = .progress(Float(progress)) + updatedProgress = true + } } } else { - Logger.shared.log("VideoExport", "No pixelbuffer from composer") - - Thread.sleep(forTimeInterval: 0.01) - self.semaphore.signal() + writer.markVideoAsFinished() + return false } - }) - self.semaphore.wait() - - position = position + CMTime(value: 1, timescale: Int32(frameRate)) - if position.seconds >= duration { - Logger.shared.log("VideoExport", "Video finished") - writer.markVideoAsFinished() - return false } - } - - self.imageArguments = (duration, frameRate, position) - - return true - } - - private func encodeVideo() -> Bool { - guard let reader = self.reader, let writer = self.writer, let output = self.videoOutput else { - return false - } - - var appendFailed = false - while writer.isReadyForMoreVideoData { - if appendFailed { - return false - } - if reader.status != .reading || writer.status != .writing { - writer.markVideoAsFinished() - return false + if let additionalVideoOutput = self.additionalVideoOutput { + if let mainTimestamp, mainTimestamp < self.configuration.additionalVideoStartTime { + + } else { + if self.skippingAdditionalCopyUpdate { + self.skippingAdditionalCopyUpdate = false + } else if let sampleBuffer = additionalVideoOutput.copyNextSampleBuffer() { + if let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) { + let timestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) + additionalInput = .videoBuffer(VideoPixelBuffer( + pixelBuffer: pixelBuffer, + rotation: self.additionalTextureRotation, + timestamp: timestamp + )) + + if !updatedProgress, let duration = self.durationValue { + let startTime = self.reader?.timeRange.start.seconds ?? 0.0 + let progress = (timestamp.seconds - startTime) / duration.seconds + self.statusValue = .progress(Float(progress)) + updatedProgress = true + } + } + if let additionalFrameRate = self.additionalFrameRate, let mainComposeFramerate = self.mainComposeFramerate { + let additionalFrameRate = round(additionalFrameRate / 30.0) * 30.0 + if Int(mainComposeFramerate) == Int(additionalFrameRate) * 2 { + self.skippingAdditionalCopyUpdate = true + } + } + } + } } - self.pauseDispatchGroup.wait() - if let sampleBuffer = output.copyNextSampleBuffer() { - let timestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) - if let duration = self.durationValue { - let startTimestamp = self.reader?.timeRange.start ?? .zero - let progress = (timestamp - startTimestamp).seconds / duration.seconds + if case let .image(image) = self.subject, let texture = self.composer?.textureForImage(image) { + mainInput = .texture(texture, self.imageArguments?.position ?? .zero) + + if !updatedProgress, let imageArguments = self.imageArguments, let duration = self.durationValue { + let progress = imageArguments.position.seconds / duration.seconds self.statusValue = .progress(Float(progress)) + updatedProgress = true } - - let additionalSampleBuffer = self.additionalVideoOutput?.copyNextSampleBuffer() - - if let composer = self.composer { - composer.processSampleBuffer(sampleBuffer: sampleBuffer, textureRotation: self.textureRotation, additionalSampleBuffer: additionalSampleBuffer, additionalTextureRotation: self.additionalTextureRotation, pool: writer.pixelBufferPool, completion: { pixelBuffer in + } + + if let composer = self.composer { + let timestamp: CMTime? + if let imageArguments = self.imageArguments { + timestamp = imageArguments.position + } else { + if case .image = self.subject { + timestamp = additionalInput?.timestamp + } else { + timestamp = mainInput?.timestamp + } + } + guard let timestamp else { + writer.markVideoAsFinished() + return false + } + composer.process( + main: mainInput!, + additional: additionalInput, + pool: writer.pixelBufferPool, + completion: { pixelBuffer in if let pixelBuffer { if !writer.appendPixelBuffer(pixelBuffer, at: timestamp) { writer.markVideoAsFinished() appendFailed = true } } else { - if !writer.appendVideoBuffer(sampleBuffer) { - writer.markVideoAsFinished() - appendFailed = true - } +// if !writer.appendVideoBuffer(sampleBuffer) { +// writer.markVideoAsFinished() +// appendFailed = true +// } + appendFailed = true } self.semaphore.signal() - }) - self.semaphore.wait() - } else { - if !writer.appendVideoBuffer(sampleBuffer) { + } + ) + self.semaphore.wait() + + if let imageArguments = self.imageArguments, let duration = self.durationValue { + let position = imageArguments.position + CMTime(value: 1, timescale: Int32(imageArguments.frameRate)) + self.imageArguments = (imageArguments.frameRate, position) + + if position.seconds >= duration.seconds { + Logger.shared.log("VideoExport", "Video finished") writer.markVideoAsFinished() return false } } } else { - writer.markVideoAsFinished() - return false +// if !writer.appendVideoBuffer(sampleBuffer) { +// writer.markVideoAsFinished() +// return false +// } } } return true } private func encodeAudio() -> Bool { - guard let reader = self.reader, let writer = self.writer, let output = self.audioOutput else { + guard let writer = self.writer, let output = self.audioOutput else { return false } while writer.isReadyForMoreAudioData { - if reader.status != .reading || writer.status != .writing { + if writer.status != .writing { writer.markAudioAsFinished() return false } @@ -858,84 +860,39 @@ public final class MediaEditorVideoExport { return true } - func pause() { - guard self.internalStatus == .exporting && self.cancelled == false else { - return - } - self.internalStatus = .paused - self.pauseDispatchGroup.enter() - } - - func resume() { - guard self.internalStatus == .paused && self.cancelled == false else { - return - } - self.internalStatus = .exporting - self.pauseDispatchGroup.leave() - } - - public func cancel() { - if case .paused = self.internalStatus { - self.resume() - } - self.cancelled = true - - self.queue.async { - if let reader = self.reader, reader.status == .reading { - reader.cancelReading() - } - } - } - - private let statusPromise = Promise(.unknown) - private var statusValue: ExportStatus = .unknown { - didSet { - self.statusPromise.set(.single(self.statusValue)) - } - } - public var status: Signal { - return self.statusPromise.get() - } - - private func startImageVideoExport() { - Logger.shared.log("VideoExport", "Starting image video export") - + public func start() { guard self.internalStatus == .idle, let writer = self.writer else { - Logger.shared.log("VideoExport", "Failed on writer state") self.statusValue = .failed(.invalid) return } guard writer.startWriting() else { - Logger.shared.log("VideoExport", "Failed on start writing") self.statusValue = .failed(.writing(nil)) return } - if let _ = self.audioOutput, let reader = self.reader { - guard reader.startReading() else { - self.statusValue = .failed(.reading(nil)) - return - } + if let reader = self.reader, !reader.startReading() { + self.statusValue = .failed(.reading(nil)) + return } - self.internalStatus = .exporting + if case .image = self.subject, self.additionalVideoOutput == nil { + self.imageArguments = (Double(self.configuration.frameRate), CMTime(value: 0, timescale: Int32(self.configuration.frameRate))) + } - writer.startSession(atSourceTime: .zero) + self.internalStatus = .exporting - var duration: Double = 5.0 - if let audioDuration = self.configuration.audioTimeRange?.duration.seconds { - duration = audioDuration + if let timeRange = self.reader?.timeRange { + print("reader timerange: \(timeRange)") } - self.imageArguments = (duration, Double(self.configuration.frameRate), CMTime(value: 0, timescale: Int32(self.configuration.frameRate))) + writer.startSession(atSourceTime: self.reader?.timeRange.start ?? .zero) var videoCompleted = false var audioCompleted = false - var exportForVideoOutput: MediaEditorVideoExport? = self writer.requestVideoDataWhenReady(on: self.queue.queue) { guard let export = exportForVideoOutput else { return } - if !export.encodeImageVideo() { + if !export.encodeVideo() { videoCompleted = true exportForVideoOutput = nil if audioCompleted { @@ -961,71 +918,117 @@ public final class MediaEditorVideoExport { } } - private func startVideoExport() { - guard self.internalStatus == .idle, let writer = self.writer, let reader = self.reader else { - self.statusValue = .failed(.invalid) + private func finish() { + assert(self.queue.isCurrent()) + + guard let writer = self.writer else { return } - guard writer.startWriting() else { - self.statusValue = .failed(.writing(nil)) - return + let outputUrl = URL(fileURLWithPath: self.outputPath) + + var cancelled = false + if let reader = self.reader, reader.status == .cancelled { + if writer.status != .cancelled { + writer.cancelWriting() + } + cancelled = true } - guard reader.startReading() else { - self.statusValue = .failed(.reading(nil)) - return + + if writer.status == .cancelled { + if let reader = self.reader, reader.status != .cancelled { + reader.cancelReading() + } + cancelled = true } - if let additionalReader = self.additionalReader, !additionalReader.startReading() { - self.statusValue = .failed(.reading(nil)) + if cancelled { + try? FileManager.default.removeItem(at: outputUrl) + self.internalStatus = .finished + self.statusValue = .failed(.cancelled) return } - self.internalStatus = .exporting - - writer.startSession(atSourceTime: self.configuration.timeRange?.start ?? .zero) - - var videoCompleted = false - var audioCompleted = false - if let _ = self.videoOutput { - var exportForVideoOutput: MediaEditorVideoExport? = self - writer.requestVideoDataWhenReady(on: self.queue.queue) { - guard let export = exportForVideoOutput else { return } - if !export.encodeVideo() { - videoCompleted = true - exportForVideoOutput = nil - if audioCompleted { - export.finish() + if writer.status == .failed { + if let error = writer.error { + Logger.shared.log("VideoExport", "Failed with writer error \(error.localizedDescription)") + } + try? FileManager.default.removeItem(at: outputUrl) + self.internalStatus = .finished + self.statusValue = .failed(.writing(nil)) + } else if let reader = self.reader, reader.status == .failed { + if let error = reader.error { + Logger.shared.log("VideoExport", "Failed with reader error \(error.localizedDescription)") + } + try? FileManager.default.removeItem(at: outputUrl) + writer.cancelWriting() + self.internalStatus = .finished + self.statusValue = .failed(.reading(reader.error)) + } else { + writer.finishWriting { + self.queue.async { + if writer.status == .failed { + if let error = writer.error { + Logger.shared.log("VideoExport", "Failed after finishWriting with writer error \(error.localizedDescription)") + } + try? FileManager.default.removeItem(at: outputUrl) + self.internalStatus = .finished + self.statusValue = .failed(.writing(nil)) + } else { + self.internalStatus = .finished + self.statusValue = .completed + + let end = CACurrentMediaTime() + let _ = (self.duration.get() + |> take(1)).start(next: { duration in + let exportDuration = end - self.startTimestamp + print("video processing took \(exportDuration)s") + if duration.seconds > 0 { + Logger.shared.log("VideoExport", "Video processing took \(exportDuration / duration.seconds)") + } + }) } } } - } else { - videoCompleted = true } + } + + func pause() { + guard self.internalStatus == .exporting && self.cancelled == false else { + return + } + self.internalStatus = .paused + self.pauseDispatchGroup.enter() + } + + func resume() { + guard self.internalStatus == .paused && self.cancelled == false else { + return + } + self.internalStatus = .exporting + self.pauseDispatchGroup.leave() + } - if let _ = self.audioOutput { - var exportForAudioOutput: MediaEditorVideoExport? = self - writer.requestAudioDataWhenReady(on: self.queue.queue) { - guard let export = exportForAudioOutput else { return } - if !export.encodeAudio() { - audioCompleted = true - exportForAudioOutput = nil - if videoCompleted { - export.finish() - } - } + public func cancel() { + if case .paused = self.internalStatus { + self.resume() + } + self.cancelled = true + + self.queue.async { + if let reader = self.reader, reader.status == .reading { + reader.cancelReading() } - } else { - audioCompleted = true } } - public func start() { - switch self.subject { - case .video: - self.startVideoExport() - case .image: - self.startImageVideoExport() + private let statusPromise = Promise(.unknown) + private var statusValue: ExportStatus = .unknown { + didSet { + self.statusPromise.set(.single(self.statusValue)) } } + public var status: Signal { + return self.statusPromise.get() + } } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/UniversalTextureSource.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/UniversalTextureSource.swift new file mode 100644 index 00000000000..737dd4ee674 --- /dev/null +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/UniversalTextureSource.swift @@ -0,0 +1,249 @@ +import Foundation +import AVFoundation +import Metal +import MetalKit + +final class UniversalTextureSource: TextureSource { + enum Input { + case image(UIImage) + case video(AVPlayerItem) + + fileprivate func createContext(renderTarget: RenderTarget, queue: DispatchQueue, additional: Bool) -> InputContext { + switch self { + case .image: + return ImageInputContext(input: self, renderTarget: renderTarget, queue: queue) + case .video: + return VideoInputContext(input: self, renderTarget: renderTarget, queue: queue, additional: additional) + } + } + } + + private weak var renderTarget: RenderTarget? + private var displayLink: CADisplayLink? + private let queue: DispatchQueue + + private var mainInputContext: InputContext? + private var additionalInputContext: InputContext? + + var forceUpdates = false + private var rate: Float = 1.0 + + weak var output: MediaEditorRenderer? + + init(renderTarget: RenderTarget) { + self.renderTarget = renderTarget + + self.queue = DispatchQueue( + label: "UniversalTextureSource Queue", + qos: .userInteractive, + attributes: [], + autoreleaseFrequency: .workItem, + target: nil + ) + } + + func setMainInput(_ input: Input) { + guard let renderTarget = self.renderTarget else { + return + } + self.mainInputContext = input.createContext(renderTarget: renderTarget, queue: self.queue, additional: false) + self.update(forced: true) + } + + func setAdditionalInput(_ input: Input?) { + guard let renderTarget = self.renderTarget else { + return + } + if let input { + self.additionalInputContext = input.createContext(renderTarget: renderTarget, queue: self.queue, additional: true) + } else { + self.additionalInputContext = nil + } + self.update(forced: true) + } + + + func setRate(_ rate: Float) { + self.rate = rate + } + + private var previousAdditionalOutput: MediaEditorRenderer.Input? + private func update(forced: Bool) { + let time = CACurrentMediaTime() + + let needsDisplayLink = (self.mainInputContext?.needsDisplayLink ?? false) || (self.additionalInputContext?.needsDisplayLink ?? false) + if needsDisplayLink { + if self.displayLink == nil { + let displayLink = CADisplayLink(target: DisplayLinkTarget({ [weak self] in + if let self { + self.update(forced: self.forceUpdates) + } + }), selector: #selector(DisplayLinkTarget.handleDisplayLinkUpdate(sender:))) + displayLink.preferredFramesPerSecond = 60 + displayLink.add(to: .main, forMode: .common) + self.displayLink = displayLink + } + } else { + if let displayLink = self.displayLink { + self.displayLink = nil + displayLink.invalidate() + } + } + + guard self.rate > 0.0 || forced else { + return + } + + let main = self.mainInputContext?.output(time: time) + var additional = self.additionalInputContext?.output(time: time) + if let additional { + self.previousAdditionalOutput = additional + } else if self.additionalInputContext != nil { + additional = self.previousAdditionalOutput + } + + guard let main else { + return + } + + self.output?.consume(main: main, additional: additional, render: true) + } + + func connect(to consumer: MediaEditorRenderer) { + self.output = consumer + self.update(forced: true) + } + + func invalidate() { + self.mainInputContext?.invalidate() + self.additionalInputContext?.invalidate() + } + + private class DisplayLinkTarget { + private let update: () -> Void + init(_ update: @escaping () -> Void) { + self.update = update + } + @objc func handleDisplayLinkUpdate(sender: CADisplayLink) { + self.update() + } + } +// +// private func setupDisplayLink(frameRate: Int) { +// self.displayLink?.invalidate() +// self.displayLink = nil +// +// if self.playerItemOutput != nil { + +// } +// } +} + +private protocol InputContext { + typealias Input = UniversalTextureSource.Input + typealias Output = MediaEditorRenderer.Input + + var input: Input { get } + func output(time: Double) -> Output? + + var needsDisplayLink: Bool { get } + + func invalidate() +} + +private class ImageInputContext: InputContext { + fileprivate var input: Input + private var texture: MTLTexture? + + init(input: Input, renderTarget: RenderTarget, queue: DispatchQueue) { + guard case let .image(image) = input else { + fatalError() + } + self.input = input + if let device = renderTarget.mtlDevice { + self.texture = loadTexture(image: image, device: device) + } + } + + func output(time: Double) -> Output? { + return self.texture.flatMap { .texture($0, .zero) } + } + + func invalidate() { + self.texture = nil + } + + var needsDisplayLink: Bool { + return false + } +} + +private class VideoInputContext: NSObject, InputContext, AVPlayerItemOutputPullDelegate { + fileprivate var input: Input + private var videoOutput: AVPlayerItemVideoOutput? + private var textureRotation: TextureRotation = .rotate0Degrees + + var playerItem: AVPlayerItem { + guard case let .video(playerItem) = self.input else { + fatalError() + } + return playerItem + } + + init(input: Input, renderTarget: RenderTarget, queue: DispatchQueue, additional: Bool) { + guard case .video = input else { + fatalError() + } + self.input = input + super.init() + + //TODO: mirror if self.additionalPlayer == nil && self.mirror + self.textureRotation = textureRotatonForAVAsset(self.playerItem.asset, mirror: additional) + + let colorProperties: [String: Any] = [ + AVVideoColorPrimariesKey: AVVideoColorPrimaries_ITU_R_709_2, + AVVideoTransferFunctionKey: AVVideoTransferFunction_ITU_R_709_2, + AVVideoYCbCrMatrixKey: AVVideoYCbCrMatrix_ITU_R_709_2 + ] + + let outputSettings: [String: Any] = [ + kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, + kCVPixelBufferMetalCompatibilityKey as String: true, + AVVideoColorPropertiesKey: colorProperties + ] + + let videoOutput = AVPlayerItemVideoOutput(outputSettings: outputSettings) + videoOutput.suppressesPlayerRendering = true + videoOutput.setDelegate(self, queue: queue) + self.playerItem.add(videoOutput) + self.videoOutput = videoOutput + } + + func output(time: Double) -> Output? { + guard let videoOutput = self.videoOutput else { + return nil + } + let requestTime = videoOutput.itemTime(forHostTime: time) + if requestTime < .zero { + return nil + } + var presentationTime: CMTime = .zero + var videoPixelBuffer: VideoPixelBuffer? + if let pixelBuffer = videoOutput.copyPixelBuffer(forItemTime: requestTime, itemTimeForDisplay: &presentationTime) { + videoPixelBuffer = VideoPixelBuffer(pixelBuffer: pixelBuffer, rotation: self.textureRotation, timestamp: presentationTime) + } + return videoPixelBuffer.flatMap { .videoBuffer($0) } + } + + func invalidate() { + if let videoOutput = self.videoOutput { + self.videoOutput = nil + self.playerItem.remove(videoOutput) + videoOutput.setDelegate(nil, queue: nil) + } + } + + var needsDisplayLink: Bool { + return true + } +} diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/VideoTextureSource.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/VideoFinishPass.swift similarity index 53% rename from submodules/TelegramUI/Components/MediaEditor/Sources/VideoTextureSource.swift rename to submodules/TelegramUI/Components/MediaEditor/Sources/VideoFinishPass.swift index 41599f10f3f..45f313016f3 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/VideoTextureSource.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/VideoFinishPass.swift @@ -2,303 +2,7 @@ import Foundation import AVFoundation import Metal import MetalKit - -func textureRotatonForAVAsset(_ asset: AVAsset, mirror: Bool = false) -> TextureRotation { - for track in asset.tracks { - if track.mediaType == .video { - let t = track.preferredTransform - if t.a == -1.0 && t.d == -1.0 { - return .rotate180Degrees - } else if t.a == 1.0 && t.d == 1.0 { - return .rotate0Degrees - } else if t.b == -1.0 && t.c == 1.0 { - return .rotate270Degrees - } else if t.a == -1.0 && t.d == 1.0 { - return .rotate270Degrees - } else if t.a == 1.0 && t.d == -1.0 { - return .rotate180Degrees - } else { - return mirror ? .rotate90DegreesMirrored : .rotate90Degrees - } - } - } - return .rotate0Degrees -} - -final class VideoTextureSource: NSObject, TextureSource, AVPlayerItemOutputPullDelegate { - private weak var player: AVPlayer? - private weak var additionalPlayer: AVPlayer? - private weak var playerItem: AVPlayerItem? - private weak var additionalPlayerItem: AVPlayerItem? - - private let mirror: Bool - - private var playerItemOutput: AVPlayerItemVideoOutput? - private var additionalPlayerItemOutput: AVPlayerItemVideoOutput? - - private var displayLink: CADisplayLink? - - private let device: MTLDevice? - private var textureRotation: TextureRotation = .rotate0Degrees - private var additionalTextureRotation: TextureRotation = .rotate0Degrees - - private var forceUpdate: Bool = false - - weak var output: TextureConsumer? - var queue: DispatchQueue! - var started: Bool = false - - init(player: AVPlayer, additionalPlayer: AVPlayer?, mirror: Bool, renderTarget: RenderTarget) { - self.player = player - self.additionalPlayer = additionalPlayer - self.mirror = mirror - self.device = renderTarget.mtlDevice! - - self.queue = DispatchQueue( - label: "VideoTextureSource Queue", - qos: .userInteractive, - attributes: [], - autoreleaseFrequency: .workItem, - target: nil) - - super.init() - - self.playerItem = player.currentItem - self.additionalPlayerItem = additionalPlayer?.currentItem - self.handleReadyToPlay() - } - - func invalidate() { - self.playerItemOutput?.setDelegate(nil, queue: nil) - self.playerItemOutput = nil - self.displayLink?.invalidate() - self.displayLink = nil - } - - private func handleReadyToPlay() { - guard let playerItem = self.playerItem else { - return - } - - var frameRate: Int = 30 - var hasVideoTrack: Bool = false - for track in playerItem.asset.tracks { - if track.mediaType == .video { - if track.nominalFrameRate > 0.0 { - frameRate = Int(ceil(track.nominalFrameRate)) - } - hasVideoTrack = true - break - } - } - self.textureRotation = textureRotatonForAVAsset(playerItem.asset, mirror: additionalPlayer == nil && mirror) - if !hasVideoTrack { - return - } - - let colorProperties: [String: Any] = [ - AVVideoColorPrimariesKey: AVVideoColorPrimaries_ITU_R_709_2, - AVVideoTransferFunctionKey: AVVideoTransferFunction_ITU_R_709_2, - AVVideoYCbCrMatrixKey: AVVideoYCbCrMatrix_ITU_R_709_2 - ] - - let outputSettings: [String: Any] = [ - kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, - kCVPixelBufferMetalCompatibilityKey as String: true, - AVVideoColorPropertiesKey: colorProperties - ] - - let output = AVPlayerItemVideoOutput(outputSettings: outputSettings) - output.suppressesPlayerRendering = true - output.setDelegate(self, queue: self.queue) - playerItem.add(output) - self.playerItemOutput = output - - if let additionalPlayerItem = self.additionalPlayerItem { - self.additionalTextureRotation = textureRotatonForAVAsset(additionalPlayerItem.asset, mirror: true) - - let output = AVPlayerItemVideoOutput(outputSettings: outputSettings) - output.suppressesPlayerRendering = true - output.setDelegate(self, queue: self.queue) - additionalPlayerItem.add(output) - self.additionalPlayerItemOutput = output - } - - self.setupDisplayLink(frameRate: min(60, frameRate)) - } - - private class DisplayLinkTarget { - private let handler: () -> Void - init(_ handler: @escaping () -> Void) { - self.handler = handler - } - @objc func handleDisplayLinkUpdate(sender: CADisplayLink) { - self.handler() - } - } - - private func setupDisplayLink(frameRate: Int) { - self.displayLink?.invalidate() - self.displayLink = nil - - if self.playerItemOutput != nil { - let displayLink = CADisplayLink(target: DisplayLinkTarget({ [weak self] in - self?.handleUpdate() - }), selector: #selector(DisplayLinkTarget.handleDisplayLinkUpdate(sender:))) - displayLink.preferredFramesPerSecond = frameRate - displayLink.add(to: .main, forMode: .common) - self.displayLink = displayLink - } - } - - private func handleUpdate() { - guard let player = self.player else { - return - } - if player.rate != 0 { - self.forceUpdate = true - } - self.update(forced: self.forceUpdate) - self.forceUpdate = false - } - - private let advanceInterval: TimeInterval = 1.0 / 60.0 - private func update(forced: Bool) { - guard let output = self.playerItemOutput else { - return - } - - let time = CACurrentMediaTime() - let requestTime = output.itemTime(forHostTime: time) - if requestTime < .zero { - return - } - - if !forced && !output.hasNewPixelBuffer(forItemTime: requestTime) { - self.displayLink?.isPaused = true - output.requestNotificationOfMediaDataChange(withAdvanceInterval: self.advanceInterval) - return - } - - var presentationTime: CMTime = .zero - var mainPixelBuffer: VideoPixelBuffer? - if let pixelBuffer = output.copyPixelBuffer(forItemTime: requestTime, itemTimeForDisplay: &presentationTime) { - mainPixelBuffer = VideoPixelBuffer(pixelBuffer: pixelBuffer, rotation: self.textureRotation, timestamp: presentationTime) - } - - let additionalRequestTime = self.additionalPlayerItemOutput?.itemTime(forHostTime: time) - var additionalPixelBuffer: VideoPixelBuffer? - if let additionalRequestTime, let pixelBuffer = self.additionalPlayerItemOutput?.copyPixelBuffer(forItemTime: additionalRequestTime, itemTimeForDisplay: &presentationTime) { - additionalPixelBuffer = VideoPixelBuffer(pixelBuffer: pixelBuffer, rotation: self.additionalTextureRotation, timestamp: presentationTime) - } - - if let mainPixelBuffer { - self.output?.consumeVideoPixelBuffer(pixelBuffer: mainPixelBuffer, additionalPixelBuffer: additionalPixelBuffer, render: true) - } - } - - func setNeedsUpdate() { - self.displayLink?.isPaused = false - self.forceUpdate = true - } - - func updateIfNeeded() { - if self.forceUpdate { - self.update(forced: true) - self.forceUpdate = false - } - } - - func connect(to consumer: TextureConsumer) { - self.output = consumer - } - - public func outputMediaDataWillChange(_ sender: AVPlayerItemOutput) { - self.displayLink?.isPaused = false - } -} - -final class VideoInputPass: DefaultRenderPass { - private var cachedTexture: MTLTexture? - - override var fragmentShaderFunctionName: String { - return "bt709ToRGBFragmentShader" - } - - override func setup(device: MTLDevice, library: MTLLibrary) { - super.setup(device: device, library: library) - } - - func processPixelBuffer(_ pixelBuffer: VideoPixelBuffer, textureCache: CVMetalTextureCache, device: MTLDevice, commandBuffer: MTLCommandBuffer) -> MTLTexture? { - func textureFromPixelBuffer(_ pixelBuffer: CVPixelBuffer, pixelFormat: MTLPixelFormat, width: Int, height: Int, plane: Int) -> MTLTexture? { - var textureRef : CVMetalTexture? - let status = CVMetalTextureCacheCreateTextureFromImage(nil, textureCache, pixelBuffer, nil, pixelFormat, width, height, plane, &textureRef) - if status == kCVReturnSuccess, let textureRef { - return CVMetalTextureGetTexture(textureRef) - } - return nil - } - - let width = CVPixelBufferGetWidth(pixelBuffer.pixelBuffer) - let height = CVPixelBufferGetHeight(pixelBuffer.pixelBuffer) - guard let inputYTexture = textureFromPixelBuffer(pixelBuffer.pixelBuffer, pixelFormat: .r8Unorm, width: width, height: height, plane: 0), - let inputCbCrTexture = textureFromPixelBuffer(pixelBuffer.pixelBuffer, pixelFormat: .rg8Unorm, width: width >> 1, height: height >> 1, plane: 1) else { - return nil - } - return self.process(yTexture: inputYTexture, cbcrTexture: inputCbCrTexture, width: width, height: height, rotation: pixelBuffer.rotation, device: device, commandBuffer: commandBuffer) - } - - func process(yTexture: MTLTexture, cbcrTexture: MTLTexture, width: Int, height: Int, rotation: TextureRotation, device: MTLDevice, commandBuffer: MTLCommandBuffer) -> MTLTexture? { - self.setupVerticesBuffer(device: device, rotation: rotation) - - func textureDimensionsForRotation(width: Int, height: Int, rotation: TextureRotation) -> (width: Int, height: Int) { - switch rotation { - case .rotate90Degrees, .rotate270Degrees, .rotate90DegreesMirrored: - return (height, width) - default: - return (width, height) - } - } - - let (outputWidth, outputHeight) = textureDimensionsForRotation(width: width, height: height, rotation: rotation) - if self.cachedTexture == nil { - let textureDescriptor = MTLTextureDescriptor() - textureDescriptor.textureType = .type2D - textureDescriptor.width = outputWidth - textureDescriptor.height = outputHeight - textureDescriptor.pixelFormat = self.pixelFormat - textureDescriptor.storageMode = .private - textureDescriptor.usage = [.shaderRead, .shaderWrite, .renderTarget] - if let texture = device.makeTexture(descriptor: textureDescriptor) { - self.cachedTexture = texture - } - } - - let renderPassDescriptor = MTLRenderPassDescriptor() - renderPassDescriptor.colorAttachments[0].texture = self.cachedTexture! - renderPassDescriptor.colorAttachments[0].loadAction = .dontCare - renderPassDescriptor.colorAttachments[0].storeAction = .store - renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0) - guard let renderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else { - return nil - } - - renderCommandEncoder.setViewport(MTLViewport( - originX: 0, originY: 0, - width: Double(outputWidth), height: Double(outputHeight), - znear: -1.0, zfar: 1.0) - ) - - renderCommandEncoder.setFragmentTexture(yTexture, index: 0) - renderCommandEncoder.setFragmentTexture(cbcrTexture, index: 1) - - self.encodeDefaultCommands(using: renderCommandEncoder) - - renderCommandEncoder.endEncoding() - - return self.cachedTexture - } -} +import SwiftSignalKit private func verticesData( textureRotation: TextureRotation, @@ -346,6 +50,8 @@ private func verticesData( bottomRight = simd_float2(1.0, 1.0) } + let containerSize = CGSize(width: containerSize.width, height: containerSize.height) + let angle = Float(.pi - rotation) let cosAngle = cos(angle) let sinAngle = sin(angle) @@ -434,14 +140,17 @@ private func lookupSpringValue(_ t: CGFloat) -> CGFloat { return 1.0 } -final class VideoInputScalePass: RenderPass { +private var transitionDuration = 0.5 +private var apperanceDuration = 0.2 +private var videoRemovalDuration: Double = 0.2 + +final class VideoFinishPass: RenderPass { private var cachedTexture: MTLTexture? + var gradientPipelineState: MTLRenderPipelineState? + var mainPipelineState: MTLRenderPipelineState? - var mainVerticesBuffer: MTLBuffer? var mainTextureRotation: TextureRotation = .rotate0Degrees - - var additionalVerticesBuffer: MTLBuffer? var additionalTextureRotation: TextureRotation = .rotate0Degrees var pixelFormat: MTLPixelFormat { @@ -449,20 +158,33 @@ final class VideoInputScalePass: RenderPass { } func setup(device: MTLDevice, library: MTLLibrary) { - let descriptor = MTLRenderPipelineDescriptor() - descriptor.vertexFunction = library.makeFunction(name: "defaultVertexShader") - descriptor.fragmentFunction = library.makeFunction(name: "dualFragmentShader") - descriptor.colorAttachments[0].pixelFormat = self.pixelFormat - descriptor.colorAttachments[0].isBlendingEnabled = true - descriptor.colorAttachments[0].rgbBlendOperation = .add - descriptor.colorAttachments[0].alphaBlendOperation = .add - descriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha - descriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha - descriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha - descriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha + let mainDescriptor = MTLRenderPipelineDescriptor() + mainDescriptor.vertexFunction = library.makeFunction(name: "defaultVertexShader") + mainDescriptor.fragmentFunction = library.makeFunction(name: "dualFragmentShader") + mainDescriptor.colorAttachments[0].pixelFormat = self.pixelFormat + mainDescriptor.colorAttachments[0].isBlendingEnabled = true + mainDescriptor.colorAttachments[0].rgbBlendOperation = .add + mainDescriptor.colorAttachments[0].alphaBlendOperation = .add + mainDescriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha + mainDescriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha + mainDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha + mainDescriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha + + let gradientDescriptor = MTLRenderPipelineDescriptor() + gradientDescriptor.vertexFunction = library.makeFunction(name: "defaultVertexShader") + gradientDescriptor.fragmentFunction = library.makeFunction(name: "gradientFragmentShader") + gradientDescriptor.colorAttachments[0].pixelFormat = self.pixelFormat + gradientDescriptor.colorAttachments[0].isBlendingEnabled = true + gradientDescriptor.colorAttachments[0].rgbBlendOperation = .add + gradientDescriptor.colorAttachments[0].alphaBlendOperation = .add + gradientDescriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha + gradientDescriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha + gradientDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha + gradientDescriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha do { - self.mainPipelineState = try device.makeRenderPipelineState(descriptor: descriptor) + self.mainPipelineState = try device.makeRenderPipelineState(descriptor: mainDescriptor) + self.gradientPipelineState = try device.makeRenderPipelineState(descriptor: gradientDescriptor) } catch { print(error.localizedDescription) } @@ -487,8 +209,8 @@ final class VideoInputScalePass: RenderPass { ) let size = CGSize( - width: position.size.width * position.scale, - height: position.size.height * position.scale + width: position.size.width * position.scale * position.baseScale, + height: position.size.height * position.scale * position.baseScale ) let vertices = verticesData(textureRotation: textureRotation, containerSize: containerSize, position: center, size: size, rotation: position.rotation, z: zPosition) @@ -510,31 +232,69 @@ final class VideoInputScalePass: RenderPass { encoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4) } - func update(values: MediaEditorValues) { + private let canvasSize = CGSize(width: 1080.0, height: 1920.0) + private var gradientColors = GradientColors(topColor: simd_float3(0.0, 0.0, 0.0), bottomColor: simd_float3(0.0, 0.0, 0.0)) + func update(values: MediaEditorValues, videoDuration: Double?, additionalVideoDuration: Double?) { + let position = CGPoint( + x: canvasSize.width / 2.0 + values.cropOffset.x, + y: canvasSize.height / 2.0 + values.cropOffset.y + ) + + self.isStory = values.isStory + self.mainPosition = VideoFinishPass.VideoPosition(position: position, size: self.mainPosition.size, scale: values.cropScale, rotation: values.cropRotation, baseScale: self.mainPosition.baseScale) + if let position = values.additionalVideoPosition, let scale = values.additionalVideoScale, let rotation = values.additionalVideoRotation { - self.additionalPosition = VideoInputScalePass.VideoPosition(position: position, size: CGSize(width: 1080.0 / 4.0, height: 1440.0 / 4.0), scale: scale, rotation: rotation) + self.additionalPosition = VideoFinishPass.VideoPosition(position: position, size: CGSize(width: 1080.0 / 4.0, height: 1440.0 / 4.0), scale: scale, rotation: rotation, baseScale: self.additionalPosition.baseScale) } if !values.additionalVideoPositionChanges.isEmpty { self.videoPositionChanges = values.additionalVideoPositionChanges } + self.videoDuration = videoDuration + self.additionalVideoDuration = additionalVideoDuration + if let videoTrimRange = values.videoTrimRange { + self.videoRange = videoTrimRange + } + if let additionalVideoTrimRange = values.additionalVideoTrimRange { + self.additionalVideoRange = additionalVideoTrimRange + } + if let additionalVideoOffset = values.additionalVideoOffset { + self.additionalVideoOffset = additionalVideoOffset + } + + if let gradientColors = values.gradientColors, let top = gradientColors.first, let bottom = gradientColors.last { + let (topRed, topGreen, topBlue, _) = top.components + let (bottomRed, bottomGreen, bottomBlue, _) = bottom.components + + self.gradientColors = GradientColors( + topColor: simd_float3(Float(topRed), Float(topGreen), Float(topBlue)), + bottomColor: simd_float3(Float(bottomRed), Float(bottomGreen), Float(bottomBlue)) + ) + } } private var mainPosition = VideoPosition( position: CGPoint(x: 1080 / 2.0, y: 1920.0 / 2.0), size: CGSize(width: 1080.0, height: 1920.0), scale: 1.0, - rotation: 0.0 + rotation: 0.0, + baseScale: 1.0 ) private var additionalPosition = VideoPosition( position: CGPoint(x: 1080 / 2.0, y: 1920.0 / 2.0), size: CGSize(width: 1440.0, height: 1920.0), scale: 0.5, - rotation: 0.0 + rotation: 0.0, + baseScale: 1.0 ) - private var transitionDuration = 0.5 + private var isStory = true private var videoPositionChanges: [VideoPositionChange] = [] + private var videoDuration: Double? + private var additionalVideoDuration: Double? + private var videoRange: Range? + private var additionalVideoRange: Range? + private var additionalVideoOffset: Double? enum VideoType { case main @@ -547,6 +307,11 @@ final class VideoInputScalePass: RenderPass { let size: CGSize let scale: CGFloat let rotation: CGFloat + let baseScale: CGFloat + + func with(size: CGSize, baseScale: CGFloat) -> VideoPosition { + return VideoPosition(position: self.position, size: size, scale: self.scale, rotation: self.rotation, baseScale: baseScale) + } func mixed(with other: VideoPosition, fraction: CGFloat) -> VideoPosition { let position = CGPoint( @@ -564,7 +329,8 @@ final class VideoInputScalePass: RenderPass { position: position, size: size, scale: scale, - rotation: rotation + rotation: rotation, + baseScale: self.baseScale ) } } @@ -577,6 +343,16 @@ final class VideoInputScalePass: RenderPass { let alpha: Float } + private var additionalVideoRemovalStartTimestamp: Double? + func animateAdditionalRemoval(completion: @escaping () -> Void) { + self.additionalVideoRemovalStartTimestamp = CACurrentMediaTime() + + Queue.mainQueue().after(videoRemovalDuration) { + completion() + self.additionalVideoRemovalStartTimestamp = nil + } + } + func transitionState(for time: CMTime, mainInput: MTLTexture, additionalInput: MTLTexture?) -> (VideoState, VideoState?, VideoState?) { let timestamp = time.seconds @@ -607,13 +383,13 @@ final class VideoInputScalePass: RenderPass { backgroundTexture = additionalInput backgroundTextureRotation = self.additionalTextureRotation - mainPosition = VideoPosition(position: mainPosition.position, size: CGSize(width: 1440.0, height: 1920.0), scale: mainPosition.scale, rotation: mainPosition.rotation) - additionalPosition = VideoPosition(position: additionalPosition.position, size: CGSize(width: 1080.0 / 4.0, height: 1920.0 / 4.0), scale: additionalPosition.scale, rotation: additionalPosition.rotation) + mainPosition = VideoPosition(position: mainPosition.position, size: CGSize(width: 1440.0, height: 1920.0), scale: mainPosition.scale, rotation: mainPosition.rotation, baseScale: mainPosition.baseScale) + additionalPosition = VideoPosition(position: additionalPosition.position, size: CGSize(width: 1080.0 / 4.0, height: 1920.0 / 4.0), scale: additionalPosition.scale, rotation: additionalPosition.rotation, baseScale: additionalPosition.baseScale) foregroundTexture = mainInput foregroundTextureRotation = self.mainTextureRotation } else { - disappearingPosition = VideoPosition(position: mainPosition.position, size: CGSize(width: 1440.0, height: 1920.0), scale: mainPosition.scale, rotation: mainPosition.rotation) + disappearingPosition = VideoPosition(position: mainPosition.position, size: CGSize(width: 1440.0, height: 1920.0), scale: mainPosition.scale, rotation: mainPosition.rotation, baseScale: mainPosition.baseScale) } if previousChange.timestamp > 0.0 && timestamp < previousChange.timestamp + transitionDuration { transitionFraction = (timestamp - previousChange.timestamp) / transitionDuration @@ -631,8 +407,8 @@ final class VideoInputScalePass: RenderPass { if transitionFraction < 1.0 { let springFraction = lookupSpringValue(transitionFraction) - let appearingPosition = VideoPosition(position: additionalPosition.position, size: additionalPosition.size, scale: 0.01, rotation: self.additionalPosition.rotation) - let backgroundInitialPosition = VideoPosition(position: additionalPosition.position, size: CGSize(width: mainPosition.size.width / 4.0, height: mainPosition.size.height / 4.0), scale: additionalPosition.scale, rotation: additionalPosition.rotation) + let appearingPosition = VideoPosition(position: additionalPosition.position, size: additionalPosition.size, scale: 0.01, rotation: self.additionalPosition.rotation, baseScale: self.additionalPosition.baseScale) + let backgroundInitialPosition = VideoPosition(position: additionalPosition.position, size: CGSize(width: mainPosition.size.width / 4.0, height: mainPosition.size.height / 4.0), scale: additionalPosition.scale, rotation: additionalPosition.rotation, baseScale: additionalPosition.baseScale) foregroundPosition = appearingPosition.mixed(with: additionalPosition, fraction: springFraction) @@ -641,37 +417,85 @@ final class VideoInputScalePass: RenderPass { foregroundAlpha = min(1.0, max(0.0, Float(transitionFraction) * 2.5)) } - foregroundVideoState = VideoState(texture: foregroundTexture, textureRotation: foregroundTextureRotation, position: foregroundPosition, roundness: 1.0, alpha: foregroundAlpha) + + var isVisible = true + var trimRangeLowerBound: Double? + var trimRangeUpperBound: Double? + if let additionalVideoRange = self.additionalVideoRange { + if let additionalVideoOffset = self.additionalVideoOffset { + trimRangeLowerBound = additionalVideoRange.lowerBound - additionalVideoOffset + trimRangeUpperBound = additionalVideoRange.upperBound - additionalVideoOffset + } else { + trimRangeLowerBound = additionalVideoRange.lowerBound + trimRangeUpperBound = additionalVideoRange.upperBound + } + } else if let additionalVideoOffset = self.additionalVideoOffset { + trimRangeLowerBound = -additionalVideoOffset + if let additionalVideoDuration = self.additionalVideoDuration { + trimRangeUpperBound = -additionalVideoOffset + additionalVideoDuration + } + } + + if (trimRangeLowerBound != nil || trimRangeUpperBound != nil), let _ = self.videoDuration { + let disappearingPosition = VideoPosition(position: foregroundPosition.position, size: foregroundPosition.size, scale: 0.01, rotation: foregroundPosition.rotation, baseScale: foregroundPosition.baseScale) + + let mainLowerBound = self.videoRange?.lowerBound ?? 0.0 + + if let trimRangeLowerBound, trimRangeLowerBound > mainLowerBound + 0.1, timestamp < trimRangeLowerBound + apperanceDuration { + let visibilityFraction = max(0.0, min(1.0, (timestamp - trimRangeLowerBound) / apperanceDuration)) + if visibilityFraction.isZero { + isVisible = false + } + foregroundAlpha = Float(visibilityFraction) + foregroundPosition = disappearingPosition.mixed(with: foregroundPosition, fraction: visibilityFraction) + } else if let trimRangeUpperBound, timestamp > trimRangeUpperBound - apperanceDuration { + let visibilityFraction = 1.0 - max(0.0, min(1.0, (timestamp - trimRangeUpperBound) / apperanceDuration)) + if visibilityFraction.isZero { + isVisible = false + } + foregroundAlpha = Float(visibilityFraction) + foregroundPosition = disappearingPosition.mixed(with: foregroundPosition, fraction: visibilityFraction) + } + } + + if isVisible { + if let additionalVideoRemovalStartTimestamp { + let disappearingPosition = VideoPosition(position: foregroundPosition.position, size: foregroundPosition.size, scale: 0.01, rotation: foregroundPosition.rotation, baseScale: foregroundPosition.baseScale) + + let visibilityFraction = max(0.0, min(1.0, 1.0 - (CACurrentMediaTime() - additionalVideoRemovalStartTimestamp) / videoRemovalDuration)) + if visibilityFraction.isZero { + isVisible = false + } + foregroundAlpha = Float(visibilityFraction) + foregroundPosition = disappearingPosition.mixed(with: foregroundPosition, fraction: visibilityFraction) + } + foregroundVideoState = VideoState(texture: foregroundTexture, textureRotation: foregroundTextureRotation, position: foregroundPosition, roundness: 1.0, alpha: foregroundAlpha) + } } return (backgroundVideoState, foregroundVideoState, disappearingVideoState) } func process(input: MTLTexture, secondInput: MTLTexture?, timestamp: CMTime, device: MTLDevice, commandBuffer: MTLCommandBuffer) -> MTLTexture? { - guard max(input.width, input.height) > 1920 || secondInput != nil else { + if !self.isStory { return input } - let scaledSize = CGSize(width: input.width, height: input.height).fitted(CGSize(width: 1920.0, height: 1920.0)) - let width: Int - let height: Int - - if secondInput != nil { - width = 1080 - height = 1920 + let baseScale: CGFloat + if input.height > input.width { + baseScale = max(canvasSize.width / CGFloat(input.width), canvasSize.height / CGFloat(input.height)) } else { - width = Int(scaledSize.width) - height = Int(scaledSize.height) + baseScale = canvasSize.width / CGFloat(input.width) } - self.mainPosition = VideoPosition(position: CGPoint(x: width / 2, y: height / 2), size: CGSize(width: width, height: height), scale: 1.0, rotation: 0.0) + self.mainPosition = self.mainPosition.with(size: CGSize(width: input.width, height: input.height), baseScale: baseScale) - let containerSize = CGSize(width: width, height: height) + let containerSize = canvasSize - if self.cachedTexture == nil || self.cachedTexture?.width != width || self.cachedTexture?.height != height { + if self.cachedTexture == nil { let textureDescriptor = MTLTextureDescriptor() textureDescriptor.textureType = .type2D - textureDescriptor.width = width - textureDescriptor.height = height + textureDescriptor.width = Int(containerSize.width) + textureDescriptor.height = Int(containerSize.height) textureDescriptor.pixelFormat = input.pixelFormat textureDescriptor.storageMode = .private textureDescriptor.usage = [.shaderRead, .shaderWrite, .renderTarget] @@ -679,7 +503,7 @@ final class VideoInputScalePass: RenderPass { return input } self.cachedTexture = texture - texture.label = "scaledVideoTexture" + texture.label = "finishedTexture" } let renderPassDescriptor = MTLRenderPassDescriptor() @@ -693,10 +517,17 @@ final class VideoInputScalePass: RenderPass { renderCommandEncoder.setViewport(MTLViewport( originX: 0, originY: 0, - width: Double(width), height: Double(height), + width: Double(containerSize.width), height: Double(containerSize.height), znear: -1.0, zfar: 1.0) ) + renderCommandEncoder.setRenderPipelineState(self.gradientPipelineState!) + self.encodeGradient( + using: renderCommandEncoder, + containerSize: containerSize, + device: device + ) + renderCommandEncoder.setRenderPipelineState(self.mainPipelineState!) let (mainVideoState, additionalVideoState, transitionVideoState) = self.transitionState(for: timestamp, mainInput: input, additionalInput: secondInput) @@ -746,6 +577,29 @@ final class VideoInputScalePass: RenderPass { return self.cachedTexture! } + struct GradientColors { + var topColor: simd_float3 + var bottomColor: simd_float3 + } + + func encodeGradient( + using encoder: MTLRenderCommandEncoder, + containerSize: CGSize, + device: MTLDevice + ) { + + let vertices = verticesDataForRotation(.rotate0Degrees) + let buffer = device.makeBuffer( + bytes: vertices, + length: MemoryLayout.stride * vertices.count, + options: []) + encoder.setVertexBuffer(buffer, offset: 0, index: 0) + + encoder.setFragmentBytes(&self.gradientColors, length: MemoryLayout.size, index: 0) + + encoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4) + } + func process(input: MTLTexture, device: MTLDevice, commandBuffer: MTLCommandBuffer) -> MTLTexture? { return nil } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/VideoInputPass.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/VideoInputPass.swift new file mode 100644 index 00000000000..98f284a0116 --- /dev/null +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/VideoInputPass.swift @@ -0,0 +1,86 @@ +import Foundation +import AVFoundation +import Metal +import MetalKit + +final class VideoInputPass: DefaultRenderPass { + private var cachedTexture: MTLTexture? + + override var fragmentShaderFunctionName: String { + return "bt709ToRGBFragmentShader" + } + + override func setup(device: MTLDevice, library: MTLLibrary) { + super.setup(device: device, library: library) + } + + func processPixelBuffer(_ pixelBuffer: VideoPixelBuffer, textureCache: CVMetalTextureCache, device: MTLDevice, commandBuffer: MTLCommandBuffer) -> MTLTexture? { + func textureFromPixelBuffer(_ pixelBuffer: CVPixelBuffer, pixelFormat: MTLPixelFormat, width: Int, height: Int, plane: Int) -> MTLTexture? { + var textureRef : CVMetalTexture? + let status = CVMetalTextureCacheCreateTextureFromImage(nil, textureCache, pixelBuffer, nil, pixelFormat, width, height, plane, &textureRef) + if status == kCVReturnSuccess, let textureRef { + return CVMetalTextureGetTexture(textureRef) + } + return nil + } + + let width = CVPixelBufferGetWidth(pixelBuffer.pixelBuffer) + let height = CVPixelBufferGetHeight(pixelBuffer.pixelBuffer) + guard let inputYTexture = textureFromPixelBuffer(pixelBuffer.pixelBuffer, pixelFormat: .r8Unorm, width: width, height: height, plane: 0), + let inputCbCrTexture = textureFromPixelBuffer(pixelBuffer.pixelBuffer, pixelFormat: .rg8Unorm, width: width >> 1, height: height >> 1, plane: 1) else { + return nil + } + return self.process(yTexture: inputYTexture, cbcrTexture: inputCbCrTexture, width: width, height: height, rotation: pixelBuffer.rotation, device: device, commandBuffer: commandBuffer) + } + + func process(yTexture: MTLTexture, cbcrTexture: MTLTexture, width: Int, height: Int, rotation: TextureRotation, device: MTLDevice, commandBuffer: MTLCommandBuffer) -> MTLTexture? { + self.setupVerticesBuffer(device: device, rotation: rotation) + + func textureDimensionsForRotation(width: Int, height: Int, rotation: TextureRotation) -> (width: Int, height: Int) { + switch rotation { + case .rotate90Degrees, .rotate270Degrees, .rotate90DegreesMirrored: + return (height, width) + default: + return (width, height) + } + } + + let (outputWidth, outputHeight) = textureDimensionsForRotation(width: width, height: height, rotation: rotation) + if self.cachedTexture == nil { + let textureDescriptor = MTLTextureDescriptor() + textureDescriptor.textureType = .type2D + textureDescriptor.width = outputWidth + textureDescriptor.height = outputHeight + textureDescriptor.pixelFormat = self.pixelFormat + textureDescriptor.storageMode = .private + textureDescriptor.usage = [.shaderRead, .shaderWrite, .renderTarget] + if let texture = device.makeTexture(descriptor: textureDescriptor) { + self.cachedTexture = texture + } + } + + let renderPassDescriptor = MTLRenderPassDescriptor() + renderPassDescriptor.colorAttachments[0].texture = self.cachedTexture! + renderPassDescriptor.colorAttachments[0].loadAction = .dontCare + renderPassDescriptor.colorAttachments[0].storeAction = .store + renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0) + guard let renderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else { + return nil + } + + renderCommandEncoder.setViewport(MTLViewport( + originX: 0, originY: 0, + width: Double(outputWidth), height: Double(outputHeight), + znear: -1.0, zfar: 1.0) + ) + + renderCommandEncoder.setFragmentTexture(yTexture, index: 0) + renderCommandEncoder.setFragmentTexture(cbcrTexture, index: 1) + + self.encodeDefaultCommands(using: renderCommandEncoder) + + renderCommandEncoder.endEncoding() + + return self.cachedTexture + } +} diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/BUILD b/submodules/TelegramUI/Components/MediaEditorScreen/BUILD index e9c288f13f1..1753e68c678 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/BUILD +++ b/submodules/TelegramUI/Components/MediaEditorScreen/BUILD @@ -47,6 +47,7 @@ swift_library( "//submodules/TelegramUI/Components/AudioWaveformComponent", "//submodules/ReactionSelectionNode", "//submodules/TelegramUI/Components/VolumeSliderContextItem", + "//submodules/TelegramUI/Components/Stories/ForwardInfoPanelComponent" ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/FlipButtonContentComponent.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/FlipButtonContentComponent.swift new file mode 100644 index 00000000000..ea71fcc02d3 --- /dev/null +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/FlipButtonContentComponent.swift @@ -0,0 +1,75 @@ +import Foundation +import UIKit +import Display +import ComponentFlow + +final class FlipButtonContentComponent: Component { + init() { + + } + + static func ==(lhs: FlipButtonContentComponent, rhs: FlipButtonContentComponent) -> Bool { + return lhs === rhs + } + + final class View: UIView { + private var component: FlipButtonContentComponent? + + private let backgroundView: BlurredBackgroundView + private let icon = SimpleLayer() + + init() { + self.backgroundView = BlurredBackgroundView(color: UIColor(white: 0.0, alpha: 0.5), enableBlur: true) + + super.init(frame: CGRect()) + + self.addSubview(self.backgroundView) + self.layer.addSublayer(self.icon) + + self.icon.contents = UIImage(bundleImageName: "Camera/FlipIcon")?.withRenderingMode(.alwaysTemplate).cgImage + } + + required init?(coder aDecoder: NSCoder) { + preconditionFailure() + } + + func playAnimation() { + let animation = CASpringAnimation(keyPath: "transform.rotation.z") + animation.fromValue = 0.0 as NSNumber + animation.toValue = CGFloat.pi as NSNumber + animation.mass = 5.0 + animation.stiffness = 900.0 + animation.damping = 90.0 + animation.duration = animation.settlingDuration + if #available(iOS 15.0, *) { + let maxFps = Float(UIScreen.main.maximumFramesPerSecond) + animation.preferredFrameRateRange = CAFrameRateRange(minimum: 30.0, maximum: maxFps, preferred: maxFps) + } + self.icon.add(animation, forKey: "transform.rotation.z") + } + + func update(component: FlipButtonContentComponent, availableSize: CGSize, transition: Transition) -> CGSize { + self.component = component + + let size = CGSize(width: 48.0, height: 48.0) + let backgroundFrame = CGRect(x: 4.0, y: 4.0, width: 40.0, height: 40.0) + + self.icon.layerTintColor = UIColor.white.cgColor + self.icon.position = CGPoint(x: size.width / 2.0, y: size.height / 2.0) + self.icon.bounds = CGRect(origin: .zero, size: size) + + self.backgroundView.frame = backgroundFrame + self.backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.width / 2.0, transition: .immediate) + + return size + } + } + + func makeView() -> View { + return View() + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorDrafts.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorDrafts.swift new file mode 100644 index 00000000000..3c6dec501ef --- /dev/null +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorDrafts.swift @@ -0,0 +1,135 @@ +import Foundation +import UIKit +import Display +import CoreLocation +import Photos +import TelegramCore +import AccountContext +import MediaEditor +import DrawingUI + +extension MediaEditorScreen { + func isEligibleForDraft() -> Bool { + if self.isEditingStory { + return false + } + guard let mediaEditor = self.node.mediaEditor else { + return false + } + let entities = self.node.entitiesView.entities.filter { !($0 is DrawingMediaEntity) } + let codableEntities = DrawingEntitiesView.encodeEntities(entities, entitiesView: self.node.entitiesView) + mediaEditor.setDrawingAndEntities(data: nil, image: mediaEditor.values.drawing, entities: codableEntities) + + let caption = self.getCaption() + + if let subject = self.node.subject, case .asset = subject, self.node.mediaEditor?.values.hasChanges == false && caption.string.isEmpty { + return false + } + return true + } + + func saveDraft(id: Int64?) { + guard let subject = self.node.subject, let mediaEditor = self.node.mediaEditor else { + return + } + try? FileManager.default.createDirectory(atPath: draftPath(engine: self.context.engine), withIntermediateDirectories: true) + + let values = mediaEditor.values + let privacy = self.state.privacy + let caption = self.getCaption() + let duration = mediaEditor.duration ?? 0.0 + + let currentTimestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) + var timestamp: Int32 + var location: CLLocationCoordinate2D? + let expiresOn: Int32 + if case let .draft(draft, _) = subject { + timestamp = draft.timestamp + location = draft.location + if let _ = id { + expiresOn = draft.expiresOn ?? currentTimestamp + 3600 * 24 * 7 + } else { + expiresOn = currentTimestamp + 3600 * 24 * 7 + } + } else { + timestamp = currentTimestamp + if case let .asset(asset) = subject { + location = asset.location?.coordinate + } + if let _ = id { + expiresOn = currentTimestamp + Int32(self.state.privacy.timeout) + } else { + expiresOn = currentTimestamp + 3600 * 24 * 7 + } + } + + if let resultImage = mediaEditor.resultImage { + mediaEditor.seek(0.0, andPlay: false) + makeEditorImageComposition(context: self.node.ciContext, postbox: self.context.account.postbox, inputImage: resultImage, dimensions: storyDimensions, values: values, time: .zero, textScale: 2.0, completion: { resultImage in + guard let resultImage else { + return + } + let fittedSize = resultImage.size.aspectFitted(CGSize(width: 128.0, height: 128.0)) + + let context = self.context + let saveImageDraft: (UIImage, PixelDimensions) -> Void = { image, dimensions in + if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) { + let path = "\(Int64.random(in: .min ... .max)).jpg" + if let data = image.jpegData(compressionQuality: 0.87) { + let draft = MediaEditorDraft(path: path, isVideo: false, thumbnail: thumbnailImage, dimensions: dimensions, duration: nil, values: values, caption: caption, privacy: privacy, timestamp: timestamp, location: location, expiresOn: expiresOn) + try? data.write(to: URL(fileURLWithPath: draft.fullPath(engine: context.engine))) + if let id { + saveStorySource(engine: context.engine, item: draft, peerId: context.account.peerId, id: id) + } else { + addStoryDraft(engine: context.engine, item: draft) + } + } + } + } + + let saveVideoDraft: (String, PixelDimensions, Double) -> Void = { videoPath, dimensions, duration in + if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) { + let path = "\(Int64.random(in: .min ... .max)).mp4" + let draft = MediaEditorDraft(path: path, isVideo: true, thumbnail: thumbnailImage, dimensions: dimensions, duration: duration, values: values, caption: caption, privacy: privacy, timestamp: timestamp, location: location, expiresOn: expiresOn) + try? FileManager.default.copyItem(atPath: videoPath, toPath: draft.fullPath(engine: context.engine)) + if let id { + saveStorySource(engine: context.engine, item: draft, peerId: context.account.peerId, id: id) + } else { + addStoryDraft(engine: context.engine, item: draft) + } + } + } + + switch subject { + case let .image(image, dimensions, _, _): + saveImageDraft(image, dimensions) + case let .video(path, _, _, _, _, dimensions, _, _, _): + saveVideoDraft(path, dimensions, duration) + case let .asset(asset): + if asset.mediaType == .video { + PHImageManager.default().requestAVAsset(forVideo: asset, options: nil) { avAsset, _, _ in + if let urlAsset = avAsset as? AVURLAsset { + saveVideoDraft(urlAsset.url.relativePath, PixelDimensions(width: Int32(asset.pixelWidth), height: Int32(asset.pixelHeight)), duration) + } + } + } else { + let options = PHImageRequestOptions() + options.deliveryMode = .highQualityFormat + PHImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .default, options: options) { image, _ in + if let image { + saveImageDraft(image, PixelDimensions(image.size)) + } + } + } + case let .draft(draft, _): + if draft.isVideo { + saveVideoDraft(draft.fullPath(engine: context.engine), draft.dimensions, draft.duration ?? 0.0) + } else if let image = UIImage(contentsOfFile: draft.fullPath(engine: context.engine)) { + saveImageDraft(image, draft.dimensions) + } + removeStoryDraft(engine: self.context.engine, path: draft.path, delete: false) + } + }) + } + } +} diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorRecording.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorRecording.swift new file mode 100644 index 00000000000..d2d30465c17 --- /dev/null +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorRecording.swift @@ -0,0 +1,167 @@ +import Foundation +import UIKit +import Display +import SwiftSignalKit +import MediaEditor +import DrawingUI +import ChatPresentationInterfaceState +import PresentationDataUtils +import TelegramPresentationData +import DeviceAccess +import AccountContext + +extension MediaEditorScreen { + final class Recording { + private weak var controller: MediaEditorScreen? + + private var recorder: EntityVideoRecorder? + + private let idleTimerExtensionDisposable = MetaDisposable() + + private var authorizationStatusDisposables = DisposableSet() + private var cameraAuthorizationStatus: AccessType = .notDetermined + private var microphoneAuthorizationStatus: AccessType = .notDetermined + + fileprivate var cameraIsActive = true { + didSet { + guard let context = self.controller?.context else { + return + } + if self.cameraIsActive { + self.idleTimerExtensionDisposable.set(context.sharedContext.applicationBindings.pushIdleTimerExtension()) + } else { + self.idleTimerExtensionDisposable.set(nil) + } + } + } + + var isLocked = false + + init(controller: MediaEditorScreen) { + self.controller = controller + + self.authorizationStatusDisposables.add((DeviceAccess.authorizationStatus(subject: .camera(.video)) + |> deliverOnMainQueue).start(next: { [weak self] status in + if let self { + self.cameraAuthorizationStatus = status + } + })) + + self.authorizationStatusDisposables.add((DeviceAccess.authorizationStatus(subject: .microphone(.video)) + |> deliverOnMainQueue).start(next: { [weak self] status in + if let self { + self.microphoneAuthorizationStatus = status + } + })) + } + + deinit { + self.idleTimerExtensionDisposable.dispose() + self.authorizationStatusDisposables.dispose() + } + + func requestDeviceAccess() { + DeviceAccess.authorizeAccess(to: .camera(.video), { granted in + if granted { + DeviceAccess.authorizeAccess(to: .microphone(.video)) + } + }) + } + + func setMediaRecordingActive(_ isActive: Bool, finished: Bool, sourceView: UIView?) { + guard let controller, let mediaEditor = controller.node.mediaEditor else { + return + } + if mediaEditor.values.additionalVideoPath != nil { + controller.node.presentVideoRemoveConfirmation() + return + } + + if isActive { + if self.cameraAuthorizationStatus != .allowed || self.microphoneAuthorizationStatus != .allowed { + self.requestDeviceAccess() + return + } + + guard self.recorder == nil else { + return + } + HapticFeedback().impact(.light) + + let recorder = EntityVideoRecorder(mediaEditor: mediaEditor, entitiesView: controller.node.entitiesView) + recorder.setup( + referenceDrawingSize: storyDimensions, + scale: 1.625, + position: PIPPosition.topRight.getPosition(storyDimensions) + ) + recorder.onAutomaticStop = { [weak self] in + if let self { + self.recorder = nil + self.controller?.node.requestLayout(forceUpdate: true, transition: .easeInOut(duration: 0.2)) + } + } + self.recorder = recorder + controller.node.requestLayout(forceUpdate: true, transition: .easeInOut(duration: 0.2)) + + self.cameraIsActive = true + } else { + if let recorder = self.recorder { + recorder.stopRecording(save: finished, completion: { [weak self] in + guard let self else { + return + } + self.recorder = nil + self.isLocked = false + self.controller?.node.requestLayout(forceUpdate: true, transition: .easeInOut(duration: 0.2)) + }) + + controller.node.requestLayout(forceUpdate: true, transition: .easeInOut(duration: 0.2)) + + self.cameraIsActive = false + } else { + guard self.tooltipController == nil, let sourceView else { + return + } + let rect = sourceView.convert(sourceView.bounds, to: nil) + let presentationData = controller.context.sharedContext.currentPresentationData.with { $0 } + let text = presentationData.strings.MediaEditor_HoldToRecordVideo + let tooltipController = TooltipController(content: .text(text), baseFontSize: presentationData.listsFontSize.baseDisplaySize, padding: 2.0) + tooltipController.dismissed = { [weak self] _ in + if let self { + self.tooltipController = nil + } + } + controller.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceViewAndRect: { [weak self] in + if let view = self?.controller?.view { + return (view, rect) + } + return nil + })) + self.tooltipController = tooltipController + } + } + } + private var tooltipController: TooltipController? + + func togglePosition() { + if let recorder = self.recorder { + recorder.togglePosition() + } + } + + var status: InstantVideoControllerRecordingStatus? { + if let recorder = self.recorder { + return InstantVideoControllerRecordingStatus( + micLevel: recorder.micLevel, + duration: recorder.duration + ) + } else { + return nil + } + } + + var isActive: Bool { + return self.status != nil + } + } +} diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 4713c45c55c..762e1f8f6bd 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -37,12 +37,7 @@ import LegacyMediaPickerUI import ReactionSelectionNode import VolumeSliderContextItem import TelegramStringFormatting - -enum DrawingScreenType { - case drawing - case text - case sticker -} +import ForwardInfoPanelComponent private let playbackButtonTag = GenericComponentViewTag() private let muteButtonTag = GenericComponentViewTag() @@ -58,6 +53,12 @@ final class MediaEditorScreenComponent: Component { } } + enum DrawingScreenType { + case drawing + case text + case sticker + } + let context: AccountContext let externalState: ExternalState let isDisplayingTool: Bool @@ -254,12 +255,14 @@ final class MediaEditorScreenComponent: Component { private let inputPanelExternalState = MessageInputPanelComponent.ExternalState() private let inputPanelBackground = ComponentView() - private let scrubber = ComponentView() + private var scrubber: ComponentView? private let playbackButton = ComponentView() private let muteButton = ComponentView() private let saveButton = ComponentView() + private let switchCameraButton = ComponentView() + private let textCancelButton = ComponentView() private let textDoneButton = ComponentView() private let textSize = ComponentView() @@ -276,13 +279,15 @@ final class MediaEditorScreenComponent: Component { private var inputMediaNodeStateContext = ChatEntityKeyboardInputNode.StateContext() private var inputMediaInteraction: ChatEntityKeyboardInputNode.Interaction? private var inputMediaNode: ChatEntityKeyboardInputNode? - - private var appliedAudioData: VideoScrubberComponent.AudioData? + + private var videoRecorder: EntityVideoRecorder? private var component: MediaEditorScreenComponent? private weak var state: State? private var environment: ViewControllerComponentContainer.Environment? + private var currentVisibleTracks: [MediaScrubberComponent.Track]? + override init(frame: CGRect) { super.init(frame: frame) @@ -495,7 +500,7 @@ final class MediaEditorScreenComponent: Component { view.layer.animateScale(from: 0.6, to: 1.0, duration: 0.2) } - if let view = self.scrubber.view { + if let view = self.scrubber?.view { view.layer.animatePosition(from: CGPoint(x: 0.0, y: 44.0), to: .zero, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, additive: true) view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) view.layer.animateScale(from: 0.6, to: 1.0, duration: 0.2) @@ -539,7 +544,7 @@ final class MediaEditorScreenComponent: Component { view.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2) } - if let view = self.scrubber.view { + if let view = self.scrubber?.view { view.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: 44.0), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) view.layer.animateAlpha(from: view.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false) view.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2) @@ -561,7 +566,7 @@ final class MediaEditorScreenComponent: Component { transition.setScale(view: view, scale: 0.1) } - if let view = self.scrubber.view { + if let view = self.scrubber?.view { view.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: 44.0), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) view.layer.animateAlpha(from: view.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false) view.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2) @@ -572,30 +577,25 @@ final class MediaEditorScreenComponent: Component { if let view = self.cancelButton.view { view.alpha = 0.0 } - let buttons = [ self.drawButton, self.textButton, self.stickerButton, self.toolsButton ] - for button in buttons { if let view = button.view { view.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: -44.0), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, additive: true) view.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2) } } - if let view = self.doneButton.view { transition.setScale(view: view, scale: 0.1) } - if let view = self.inputPanel.view { view.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2) } - - if let view = self.scrubber.view { + if let view = self.scrubber?.view { view.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2) } } @@ -604,34 +604,28 @@ final class MediaEditorScreenComponent: Component { if let view = self.cancelButton.view { view.alpha = 1.0 } - if let buttonView = self.cancelButton.view as? Button.View, let view = buttonView.content as? LottieAnimationComponent.View { view.playOnce() } - let buttons = [ self.drawButton, self.textButton, self.stickerButton, self.toolsButton ] - for button in buttons { if let view = button.view { view.layer.animatePosition(from: CGPoint(x: 0.0, y: -44.0), to: .zero, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, additive: true) view.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2) } } - if let view = self.doneButton.view { transition.setScale(view: view, scale: 1.0) } - if let view = self.inputPanel.view { view.layer.animateScale(from: 0.0, to: 1.0, duration: 0.2) } - - if let view = self.scrubber.view { + if let view = self.scrubber?.view { view.layer.animateScale(from: 0.0, to: 1.0, duration: 0.2) } } @@ -653,6 +647,9 @@ final class MediaEditorScreenComponent: Component { return availableSize } let environment = environment[ViewControllerComponentContainer.Environment.self].value + guard let controller = environment.controller() as? MediaEditorScreen else { + return availableSize + } self.environment = environment var transition = transition @@ -661,17 +658,16 @@ final class MediaEditorScreenComponent: Component { transition = transition.withUserData(nextTransitionUserData) } - var isEditingStory = false - if let controller = environment.controller() as? MediaEditorScreen { - isEditingStory = controller.isEditingStory - if self.component == nil { - if let initialCaption = controller.initialCaption { - self.inputPanelExternalState.initialText = initialCaption - } else if case let .draft(draft, _) = controller.node.subject { - self.inputPanelExternalState.initialText = draft.caption - } + let isEditingStory = controller.isEditingStory + if self.component == nil { + if let initialCaption = controller.initialCaption { + self.inputPanelExternalState.initialText = initialCaption + } else if case let .draft(draft, _) = controller.node.subject { + self.inputPanelExternalState.initialText = draft.caption } } + + let isRecordingAdditionalVideo = controller.node.recording.isActive self.component = component self.state = state @@ -701,6 +697,10 @@ final class MediaEditorScreenComponent: Component { } } + let topButtonsAlpha: CGFloat = isRecordingAdditionalVideo ? 0.3 : 1.0 + let bottomButtonsAlpha: CGFloat = isRecordingAdditionalVideo ? 0.3 : 1.0 + let buttonsAreHidden = component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities + let cancelButtonSize = self.cancelButton.update( transition: transition, component: AnyComponent(Button( @@ -715,8 +715,11 @@ final class MediaEditorScreenComponent: Component { size: CGSize(width: 33.0, height: 33.0) ) ), - action: { - guard let controller = environment.controller() as? MediaEditorScreen else { + action: { [weak self] in + guard let environment = self?.environment, let controller = environment.controller() as? MediaEditorScreen else { + return + } + guard !controller.node.recording.isActive else { return } controller.maybePresentDiscardAlert() @@ -735,14 +738,10 @@ final class MediaEditorScreenComponent: Component { } transition.setPosition(view: cancelButtonView, position: cancelButtonFrame.center) transition.setBounds(view: cancelButtonView, bounds: CGRect(origin: .zero, size: cancelButtonFrame.size)) - transition.setAlpha(view: cancelButtonView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0) - } - - var doneButtonTitle = environment.strings.Story_Editor_Next - if let controller = environment.controller() as? MediaEditorScreen, controller.isEditingStory { - doneButtonTitle = environment.strings.Story_Editor_Done + transition.setAlpha(view: cancelButtonView, alpha: buttonsAreHidden ? 0.0 : bottomButtonsAlpha) } + let doneButtonTitle = isEditingStory ? environment.strings.Story_Editor_Done : environment.strings.Story_Editor_Next let doneButtonSize = self.doneButton.update( transition: transition, component: AnyComponent(PlainButtonComponent( @@ -751,8 +750,11 @@ final class MediaEditorScreenComponent: Component { icon: UIImage(bundleImageName: "Media Editor/Next")!, title: doneButtonTitle.uppercased())), effectAlignment: .center, - action: { - guard let controller = environment.controller() as? MediaEditorScreen else { + action: { [weak self] in + guard let environment = self?.environment, let controller = environment.controller() as? MediaEditorScreen else { + return + } + guard !controller.node.recording.isActive else { return } guard controller.checkCaptionLimit() else { @@ -782,7 +784,7 @@ final class MediaEditorScreenComponent: Component { } transition.setPosition(view: doneButtonView, position: doneButtonFrame.center) transition.setBounds(view: doneButtonView, bounds: CGRect(origin: .zero, size: doneButtonFrame.size)) - transition.setAlpha(view: doneButtonView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0) + transition.setAlpha(view: doneButtonView, alpha: buttonsAreHidden ? 0.0 : bottomButtonsAlpha) } let buttonsAvailableWidth: CGFloat @@ -802,7 +804,13 @@ final class MediaEditorScreenComponent: Component { image: state.image(.draw), size: CGSize(width: 30.0, height: 30.0) )), - action: { + action: { [weak self] in + guard let environment = self?.environment, let controller = environment.controller() as? MediaEditorScreen else { + return + } + guard !controller.node.recording.isActive else { + return + } openDrawing(.drawing) } )), @@ -820,7 +828,7 @@ final class MediaEditorScreenComponent: Component { transition.setPosition(view: drawButtonView, position: drawButtonFrame.center) transition.setBounds(view: drawButtonView, bounds: CGRect(origin: .zero, size: drawButtonFrame.size)) if !self.animatingButtons { - transition.setAlpha(view: drawButtonView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0) + transition.setAlpha(view: drawButtonView, alpha: buttonsAreHidden ? 0.0 : bottomButtonsAlpha) } } @@ -831,7 +839,13 @@ final class MediaEditorScreenComponent: Component { image: state.image(.text), size: CGSize(width: 30.0, height: 30.0) )), - action: { + action: { [weak self] in + guard let environment = self?.environment, let controller = environment.controller() as? MediaEditorScreen else { + return + } + guard !controller.node.recording.isActive else { + return + } openDrawing(.text) } )), @@ -849,7 +863,7 @@ final class MediaEditorScreenComponent: Component { transition.setPosition(view: textButtonView, position: textButtonFrame.center) transition.setBounds(view: textButtonView, bounds: CGRect(origin: .zero, size: textButtonFrame.size)) if !self.animatingButtons { - transition.setAlpha(view: textButtonView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0) + transition.setAlpha(view: textButtonView, alpha: buttonsAreHidden ? 0.0 : bottomButtonsAlpha) } } @@ -860,7 +874,13 @@ final class MediaEditorScreenComponent: Component { image: state.image(.sticker), size: CGSize(width: 30.0, height: 30.0) )), - action: { + action: { [weak self] in + guard let environment = self?.environment, let controller = environment.controller() as? MediaEditorScreen else { + return + } + guard !controller.node.recording.isActive else { + return + } openDrawing(.sticker) } )), @@ -878,7 +898,7 @@ final class MediaEditorScreenComponent: Component { transition.setPosition(view: stickerButtonView, position: stickerButtonFrame.center) transition.setBounds(view: stickerButtonView, bounds: CGRect(origin: .zero, size: stickerButtonFrame.size)) if !self.animatingButtons { - transition.setAlpha(view: stickerButtonView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0) + transition.setAlpha(view: stickerButtonView, alpha: buttonsAreHidden ? 0.0 : bottomButtonsAlpha) } } @@ -889,7 +909,13 @@ final class MediaEditorScreenComponent: Component { image: state.image(.tools), size: CGSize(width: 30.0, height: 30.0) )), - action: { + action: { [weak self] in + guard let environment = self?.environment, let controller = environment.controller() as? MediaEditorScreen else { + return + } + guard !controller.node.recording.isActive else { + return + } openTools() } )), @@ -907,42 +933,12 @@ final class MediaEditorScreenComponent: Component { transition.setPosition(view: toolsButtonView, position: toolsButtonFrame.center) transition.setBounds(view: toolsButtonView, bounds: CGRect(origin: .zero, size: toolsButtonFrame.size)) if !self.animatingButtons { - transition.setAlpha(view: toolsButtonView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0) + transition.setAlpha(view: toolsButtonView, alpha: buttonsAreHidden ? 0.0 : bottomButtonsAlpha) } } - var mediaEditor: MediaEditor? - if let controller = environment.controller() as? MediaEditorScreen { - mediaEditor = controller.node.mediaEditor - } - let previousAudioData = self.appliedAudioData - var audioData: VideoScrubberComponent.AudioData? - if let audioTrack = mediaEditor?.values.audioTrack { - let artist = audioTrack.artist - var title = audioTrack.title - if artist == nil && title == nil { - if let underscoreIndex = audioTrack.path.firstIndex(of: "_"), let dotIndex = audioTrack.path.lastIndex(of: ".") { - title = String(audioTrack.path[audioTrack.path.index(after: underscoreIndex)..? + if let (forwardAuthor, forwardStory) = controller.forwardSource, !forwardStory.text.isEmpty { + let authorName = forwardAuthor.displayTitle(strings: environment.strings, displayOrder: .firstLast) + header = AnyComponent( + ForwardInfoPanelComponent( + authorName: authorName, + text: forwardStory.text, + isChannel: forwardAuthor.id.isGroupOrChannel, + isVibrant: true, + fillsWidth: true + ) + ) + } + let nextInputMode: MessageInputPanelComponent.InputMode switch self.currentInputMode { case .text: @@ -1070,6 +1080,13 @@ final class MediaEditorScreenComponent: Component { nextInputMode = .emoji } + var canRecordVideo = true + if let subject = controller.node.subject { + if case let .video(_, _, _, additionalPath, _, _, _, _, _) = subject, additionalPath != nil { + canRecordVideo = false + } + } + self.inputPanel.parentState = state let inputPanelSize = self.inputPanel.update( transition: transition, @@ -1086,14 +1103,14 @@ final class MediaEditorScreenComponent: Component { resetInputContents: nil, nextInputMode: { _ in return nextInputMode }, areVoiceMessagesAvailable: false, - presentController: { [weak self] c in - guard let self, let _ = self.component, let environment = self.environment, let controller = environment.controller() as? MediaEditorScreen else { + presentController: { [weak controller] c in + guard let controller else { return } controller.present(c, in: .window(.root)) }, - presentInGlobalOverlay: {[weak self] c in - guard let self, let _ = self.component, let environment = self.environment, let controller = environment.controller() as? MediaEditorScreen else { + presentInGlobalOverlay: { [weak controller] c in + guard let controller else { return } controller.presentInGlobalOverlay(c) @@ -1106,9 +1123,25 @@ final class MediaEditorScreenComponent: Component { }, sendMessageOptionsAction: nil, sendStickerAction: { _ in }, - setMediaRecordingActive: nil, - lockMediaRecording: nil, - stopAndPreviewMediaRecording: nil, + setMediaRecordingActive: canRecordVideo ? { [weak controller] isActive, _, finished, sourceView in + guard let controller else { + return + } + controller.node.recording.setMediaRecordingActive(isActive, finished: finished, sourceView: sourceView) + } : nil, + lockMediaRecording: { [weak controller, weak self] in + guard let controller, let self else { + return + } + controller.node.recording.isLocked = true + self.state?.updated(transition: .easeInOut(duration: 0.2)) + }, + stopAndPreviewMediaRecording: { [weak controller] in + guard let controller else { + return + } + controller.node.recording.setMediaRecordingActive(false, finished: true, sourceView: nil) + }, discardMediaRecordingPreview: nil, attachmentAction: nil, myReaction: nil, @@ -1131,8 +1164,8 @@ final class MediaEditorScreenComponent: Component { } } }, - timeoutAction: isEditingStory ? nil : { [weak self] view, gesture in - guard let self, let controller = self.environment?.controller() as? MediaEditorScreen else { + timeoutAction: isEditingStory ? nil : { [weak controller] view, gesture in + guard let controller else { return } let context = controller.context @@ -1150,20 +1183,20 @@ final class MediaEditorScreenComponent: Component { forwardAction: nil, moreAction: nil, presentVoiceMessagesUnavailableTooltip: nil, - presentTextLengthLimitTooltip: { [weak self] in - guard let self, let controller = self.environment?.controller() as? MediaEditorScreen else { + presentTextLengthLimitTooltip: { [weak controller] in + guard let controller else { return } - controller.presentCaptionLimitPremiumSuggestion(isPremium: self.state?.isPremium ?? false) + controller.presentCaptionLimitPremiumSuggestion(isPremium: controller.context.isPremium) }, - presentTextFormattingTooltip: { [weak self] in - guard let self, let controller = self.environment?.controller() as? MediaEditorScreen else { + presentTextFormattingTooltip: { [weak controller] in + guard let controller else { return } controller.presentCaptionEntitiesPremiumSuggestion() }, - paste: { [weak self] data in - guard let self, let environment = self.environment, let controller = environment.controller() as? MediaEditorScreen else { + paste: { [weak self, weak controller] data in + guard let self, let controller else { return } switch data { @@ -1191,8 +1224,9 @@ final class MediaEditorScreenComponent: Component { } }, audioRecorder: nil, - videoRecordingStatus: nil, - isRecordingLocked: false, + videoRecordingStatus: controller.node.recording.status, + isRecordingLocked: controller.node.recording.isLocked, + hasRecordedVideo: mediaEditor?.values.additionalVideoPath != nil, recordedAudioPreview: nil, hasRecordedVideoPreview: false, wasRecordingDismissed: false, @@ -1205,6 +1239,7 @@ final class MediaEditorScreenComponent: Component { customInputView: nil, forceIsEditing: self.currentInputMode == .emoji, disabledPlaceholder: nil, + header: header, isChannel: false, storyItem: nil, chatLocation: nil @@ -1213,13 +1248,9 @@ final class MediaEditorScreenComponent: Component { containerSize: CGSize(width: inputPanelAvailableWidth, height: inputPanelAvailableHeight) ) - if self.inputPanelExternalState.isEditing { - if let controller = self.environment?.controller() as? MediaEditorScreen { - if controller.node.entitiesView.hasSelection { - Queue.mainQueue().justDispatch { - controller.node.entitiesView.selectEntity(nil) - } - } + if self.inputPanelExternalState.isEditing && controller.node.entitiesView.hasSelection { + Queue.mainQueue().justDispatch { + controller.node.entitiesView.selectEntity(nil) } } @@ -1243,12 +1274,10 @@ final class MediaEditorScreenComponent: Component { self.isEditingCaption = isEditingCaption if isEditingCaption { - if let controller = environment.controller() as? MediaEditorScreen { - controller.dismissAllTooltips() - } - mediaEditor?.stop() + controller.dismissAllTooltips() + mediaEditor?.maybePauseVideo() } else { - mediaEditor?.play() + mediaEditor?.maybeUnpauseVideo() } } @@ -1291,16 +1320,12 @@ final class MediaEditorScreenComponent: Component { transition.setAlpha(view: inputPanelView, alpha: isEditingTextEntity || component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0) } - var bottomControlsTransition = transition if let playerState = state.playerState { let scrubberInset: CGFloat = 9.0 - if (audioData == nil) != (previousAudioData == nil) { - bottomControlsTransition = .easeInOut(duration: 0.25) - } let minDuration: Double let maxDuration: Double - if let mediaEditor, !mediaEditor.sourceIsVideo { + if playerState.isAudioOnly { minDuration = 5.0 maxDuration = 15.0 } else { @@ -1308,72 +1333,104 @@ final class MediaEditorScreenComponent: Component { maxDuration = storyMaxVideoDuration } - let isAudioOnly = mediaEditor?.sourceIsVideo == false - let scrubberSize = self.scrubber.update( - transition: transition, - component: AnyComponent(VideoScrubberComponent( + let previousTrackCount = self.currentVisibleTracks?.count + let visibleTracks = playerState.tracks.filter { $0.visibleInTimeline }.map { MediaScrubberComponent.Track($0) } + self.currentVisibleTracks = visibleTracks + + var scrubberTransition = transition + if let previousTrackCount, previousTrackCount != visibleTracks.count { + scrubberTransition = .easeInOut(duration: 0.2) + } + + let isAudioOnly = playerState.isAudioOnly + let hasMainVideoTrack = playerState.tracks.contains(where: { $0.id == 0 }) + + let scrubber: ComponentView + if let current = self.scrubber { + scrubber = current + } else { + scrubber = ComponentView() + self.scrubber = scrubber + } + + let scrubberSize = scrubber.update( + transition: scrubberTransition, + component: AnyComponent(MediaScrubberComponent( context: component.context, generationTimestamp: playerState.generationTimestamp, - audioOnly: isAudioOnly, - duration: playerState.duration, - startPosition: playerState.timeRange?.lowerBound ?? 0.0, - endPosition: playerState.timeRange?.upperBound ?? min(playerState.duration, storyMaxVideoDuration), position: playerState.position, minDuration: minDuration, maxDuration: maxDuration, isPlaying: playerState.isPlaying, - frames: playerState.frames, - framesUpdateTimestamp: playerState.framesUpdateTimestamp, - audioData: audioData, - videoTrimUpdated: { [weak mediaEditor] start, end, updatedEnd, done in + tracks: visibleTracks, + positionUpdated: { [weak mediaEditor] position, apply in if let mediaEditor { - mediaEditor.setVideoTrimRange(start..() - fileprivate let ciContext = CIContext(options: [.workingColorSpace : NSNull()]) + let ciContext = CIContext(options: [.workingColorSpace : NSNull()]) private let stickerPickerInputData = Promise() @@ -1884,6 +1979,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate private var playbackPositionDisposable: Disposable? + + var recording: MediaEditorScreen.Recording + private var presentationData: PresentationData private var validLayout: ContainerViewLayout? @@ -1929,6 +2027,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.selectionContainerView = DrawingSelectionContainerView(frame: .zero) self.entitiesView.selectionContainerView = self.selectionContainerView + self.recording = MediaEditorScreen.Recording(controller: controller) + super.init() self.backgroundColor = .clear @@ -1937,6 +2037,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.view.addSubview(self.containerView) self.containerView.addSubview(self.previewContainerView) self.previewContainerView.addSubview(self.gradientView) + self.previewContainerView.addSubview(self.previewView) self.previewContainerView.addSubview(self.entitiesContainerView) self.entitiesContainerView.addSubview(self.entitiesView) self.entitiesView.addSubview(self.drawingView) @@ -2007,11 +2108,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.appInForegroundDisposable = (controller.context.sharedContext.applicationBindings.applicationInForeground |> deliverOnMainQueue).start(next: { [weak self] inForeground in if let self, let mediaEditor = self.mediaEditor { - if inForeground && self.wasPlaying { - mediaEditor.play() - } else if !inForeground { - self.wasPlaying = mediaEditor.isPlaying - mediaEditor.stop() + if inForeground { + mediaEditor.maybeUnpauseVideo() + } else { + mediaEditor.maybePauseVideo() } } }) @@ -2030,6 +2130,18 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.controller?.push(c) } } + self.entitiesView.externalEntityRemoved = { [weak self] entity in + if let self, let stickerEntity = entity as? DrawingStickerEntity, case let .dualVideoReference(isAdditional) = stickerEntity.content, isAdditional { + self.mediaEditor?.setAdditionalVideo(nil, positionChanges: []) + } + } + self.entitiesView.canInteract = { [weak self] in + if let self, let controller = self.controller { + return !controller.node.recording.isActive + } else { + return true + } + } self.availableReactionsDisposable = (allowedStoryReactions(context: controller.context) |> deliverOnMainQueue).start(next: { [weak self] reactions in @@ -2061,7 +2173,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let isSavingAvailable: Bool switch subject { case .image, .video: - isSavingAvailable = !controller.isEditingStory + isSavingAvailable = !controller.isEmbeddedEditor isFromCamera = true case .draft: isSavingAvailable = true @@ -2109,7 +2221,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if let entityView = self.entitiesView.getView(for: mediaEntity.uuid) as? DrawingMediaEntityView { self.entitiesView.sendSubviewToBack(entityView) - entityView.previewView = self.previewView +// entityView.previewView = self.previewView entityView.updated = { [weak self, weak mediaEntity] in if let self, let mediaEntity { let rotationDelta = mediaEntity.rotation - initialRotation @@ -2163,13 +2275,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } else if case let .video(_, _, mirror, additionalVideoPath, _, _, _, changes, position) = subject { mediaEditor.setVideoIsMirrored(mirror) if let additionalVideoPath { - let videoEntity = DrawingStickerEntity(content: .dualVideoReference) + let videoEntity = DrawingStickerEntity(content: .dualVideoReference(false)) videoEntity.referenceDrawingSize = storyDimensions videoEntity.scale = 1.625 videoEntity.position = position.getPosition(storyDimensions) self.entitiesView.add(videoEntity, announce: false) - mediaEditor.setAdditionalVideo(additionalVideoPath, positionChanges: changes.map { VideoPositionChange(additional: $0.0, timestamp: $0.1) }) + mediaEditor.setAdditionalVideo(additionalVideoPath, isDual: true, positionChanges: changes.map { VideoPositionChange(additional: $0.0, timestamp: $0.1) }) mediaEditor.setAdditionalVideoPosition(videoEntity.position, scale: videoEntity.scale, rotation: videoEntity.rotation) if let entityView = self.entitiesView.getView(for: videoEntity.uuid) as? DrawingStickerEntityView { entityView.updated = { [weak videoEntity, weak self] in @@ -2183,12 +2295,11 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.gradientColorsDisposable = mediaEditor.gradientColors.start(next: { [weak self] colors in if let self, let colors { - let (topColor, bottomColor) = colors - let gradientImage = generateGradientImage(size: CGSize(width: 5.0, height: 640.0), colors: [topColor, bottomColor], locations: [0.0, 1.0]) + let gradientImage = generateGradientImage(size: CGSize(width: 5.0, height: 640.0), colors: colors.array, locations: [0.0, 1.0]) Queue.mainQueue().async { self.gradientView.image = gradientImage - if self.controller?.isEditingStory == true { + if self.controller?.isEmbeddedEditor == true { } else { self.previewContainerView.alpha = 1.0 @@ -2209,7 +2320,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.mediaEditor = mediaEditor self.mediaEditorPromise.set(.single(mediaEditor)) - if controller.isEditingStory == true { + if controller.isEmbeddedEditor == true { mediaEditor.onFirstDisplay = { [weak self] in if let self { if subject.isPhoto { @@ -2303,15 +2414,15 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if let self { if let selectedEntityView = self.entitiesView.selectedEntityView as? DrawingStickerEntityView, let entity = selectedEntityView.entity as? DrawingStickerEntity, case .dualVideoReference = entity.content { if isInteracting { - self.mediaEditor?.stop() + self.mediaEditor?.maybePauseVideo() } else { - self.mediaEditor?.play() + self.mediaEditor?.maybeUnpauseVideo() } } else if self.mediaEditor?.sourceIsVideo == true { if isInteracting { - self.mediaEditor?.stop() + self.mediaEditor?.maybePauseVideo() } else { - self.mediaEditor?.play() + self.mediaEditor?.maybeUnpauseVideo() } } self.isInteractingWithEntities = isInteracting @@ -2341,37 +2452,30 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } } }, + shouldDeleteEntity: { [weak self] entity in + if let self { + if let stickerEntity = entity as? DrawingStickerEntity, case .dualVideoReference(true) = stickerEntity.content { + self.presentVideoRemoveConfirmation() + return false + } + } + return true + }, getCurrentImage: { [weak self] in - guard let self else { + guard let mediaEditor = self?.mediaEditor else { return nil } let colorSpace = CGColorSpaceCreateDeviceRGB() let imageSize = CGSize(width: 1080, height: 1920) - let context = DrawingContext(size: imageSize, scale: 1.0, opaque: true, colorSpace: colorSpace) - - context?.withFlippedContext { context in - if let gradientImage = self.gradientView.image?.cgImage { - context.draw(gradientImage, in: CGRect(origin: .zero, size: imageSize)) - } - if let image = self.mediaEditor?.resultImage, let values = self.mediaEditor?.values { - let initialScale: CGFloat - if image.size.height > image.size.width { - initialScale = max(imageSize.width / image.size.width, imageSize.height / image.size.height) - } else { - initialScale = imageSize.width / image.size.width - } - let scale = initialScale * values.cropScale - context.translateBy(x: imageSize.width / 2.0 + values.cropOffset.x, y: imageSize.height / 2.0 - values.cropOffset.y) - context.rotate(by: -values.cropRotation) - context.scaleBy(x: scale, y: scale) - - if let cgImage = image.cgImage { - context.draw(cgImage, in: CGRect(x: -image.size.width / 2.0, y: -image.size.height / 2.0, width: image.size.width, height: image.size.height)) + if let context = DrawingContext(size: imageSize, scale: 1.0, opaque: true, colorSpace: colorSpace) { + context.withFlippedContext { context in + if let image = mediaEditor.resultImage?.cgImage { + context.draw(image, in: CGRect(origin: .zero, size: imageSize)) } } + return context.generateImage(colorSpace: colorSpace) } - - return context?.generateImage(colorSpace: colorSpace) + return nil }, getControllerNode: { [weak self] in return self @@ -2410,7 +2514,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } if gestureRecognizer === self.dismissPanGestureRecognizer { let location = gestureRecognizer.location(in: self.entitiesView) - if self.controller?.isEditingStory == true || self.isDisplayingTool || self.entitiesView.hasSelection || self.entitiesView.getView(at: location) != nil { + if self.controller?.isEmbeddedEditor == true || self.isDisplayingTool || self.entitiesView.hasSelection || self.entitiesView.getView(at: location) != nil { return false } return true @@ -2515,6 +2619,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } @objc func handleTap(_ gestureRecognizer: UITapGestureRecognizer) { + guard !self.recording.isActive else { + return + } let location = gestureRecognizer.location(in: self.view) var entitiesHitTestResult = self.entitiesView.hitTest(self.view.convert(location, to: self.entitiesView), with: nil) if entitiesHitTestResult is DrawingMediaEntityView { @@ -2834,7 +2941,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate completion() }) } else { - if controller.isEditingStory { + if controller.isEmbeddedEditor { if let view = self.componentHost.view as? MediaEditorScreenComponent.View { view.animateOut(to: .gallery) } @@ -2875,7 +2982,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate private weak var muteTooltip: ViewController? func presentMutedTooltip() { - guard let sourceView = self.componentHost.findTaggedView(tag: muteButtonTag) else { + guard let mediaEditor = self.mediaEditor, let sourceView = self.componentHost.findTaggedView(tag: muteButtonTag) else { return } @@ -2891,7 +2998,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.maxY + 3.0), size: CGSize()) let text: String - if let _ = self.mediaEditor?.values.audioTrack { + if mediaEditor.values.audioTrack != nil || (mediaEditor.sourceIsVideo && mediaEditor.values.additionalVideoPath != nil) { if isMuted { text = self.presentationData.strings.Story_Editor_TooltipMutedWithAudio } else { @@ -3264,13 +3371,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate var audioTrimRange: Range? var audioOffset: Double? - if mediaEditor.sourceIsVideo { - if let videoDuration = mediaEditor.originalDuration { - if let videoStart = mediaEditor.values.videoTrimRange?.lowerBound { - audioOffset = -videoStart - } - audioTrimRange = 0 ..< min(videoDuration, audioDuration) + if let videoDuration = mediaEditor.originalDuration { + if let videoStart = mediaEditor.values.videoTrimRange?.lowerBound { + audioOffset = -videoStart + } else if let _ = mediaEditor.values.additionalVideoPath, let videoStart = mediaEditor.values.additionalVideoTrimRange?.lowerBound { + audioOffset = -videoStart } + audioTrimRange = 0 ..< min(videoDuration, audioDuration) } else { audioTrimRange = 0 ..< min(15, audioDuration) } @@ -3296,34 +3403,92 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate }), in: .window(.root)) } - func presentAudioOptions(sourceView: UIView) { - let value = self.mediaEditor?.values.audioTrackVolume ?? 1.0 - let items: [ContextMenuItem] = [ - .custom(VolumeSliderContextItem(minValue: 0.0, value: value, valueChanged: { [weak self] value, _ in - if let self { - self.mediaEditor?.setAudioTrackVolume(value) + func presentVideoRemoveConfirmation() { + guard let controller = self.controller else { + return + } + let presentationData = controller.context.sharedContext.currentPresentationData.with { $0 } + let alertController = textAlertController( + context: controller.context, + forceTheme: defaultDarkColorPresentationTheme, + title: nil, + text: presentationData.strings.MediaEditor_VideoRemovalConfirmation, + actions: [ + TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { + }), + TextAlertAction(type: .destructiveAction, title: presentationData.strings.Common_Delete, action: { [weak mediaEditor, weak entitiesView] in + mediaEditor?.setAdditionalVideo(nil, positionChanges: []) + if let entityView = entitiesView?.getView(where: { entityView in + if let entity = entityView.entity as? DrawingStickerEntity, case .dualVideoReference = entity.content { + return true + } else { + return false + } + }) { + entitiesView?.remove(uuid: entityView.entity.uuid, animated: false) + } + }) + ] + ) + controller.present(alertController, in: .window(.root)) + } + + func presentTrackOptions(trackId: Int32, sourceView: UIView) { + let isVideo = trackId != 2 + let actionTitle: String = isVideo ? self.presentationData.strings.MediaEditor_RemoveVideo : self.presentationData.strings.MediaEditor_RemoveAudio + + let value: CGFloat + if trackId == 0 { + value = self.mediaEditor?.values.videoVolume ?? 1.0 + } else if trackId == 1 { + value = self.mediaEditor?.values.additionalVideoVolume ?? 1.0 + } else if trackId == 2 { + value = self.mediaEditor?.values.audioTrackVolume ?? 1.0 + } else { + value = 1.0 + } + + var items: [ContextMenuItem] = [] + items.append( + .custom(VolumeSliderContextItem(minValue: 0.0, maxValue: 1.5, value: value, valueChanged: { [weak self] value, _ in + if let self, let mediaEditor = self.mediaEditor { + if trackId == 0 { + if mediaEditor.values.videoIsMuted { + mediaEditor.setVideoIsMuted(false) + } + mediaEditor.setVideoVolume(value) + } else if trackId == 1 { + mediaEditor.setAdditionalVideoVolume(value) + } else if trackId == 2 { + mediaEditor.setAudioTrackVolume(value) + } } - }), false), - .action( - ContextMenuActionItem( - text: self.presentationData.strings.MediaEditor_RemoveAudio, - icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.primaryColor)}, - action: { [weak self] f in - f.dismissWithResult(.default) - if let self { - if let mediaEditor = self.mediaEditor { - mediaEditor.setAudioTrack(nil) - - if !mediaEditor.sourceIsVideo && !mediaEditor.isPlaying { - mediaEditor.play() + }), false) + ) + if trackId != 0 { + items.append( + .action( + ContextMenuActionItem( + text: actionTitle, + icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.primaryColor)}, + action: { [weak self] f in + f.dismissWithResult(.default) + if let self, let mediaEditor = self.mediaEditor { + if trackId == 1 { + self.presentVideoRemoveConfirmation() + } else { + mediaEditor.setAudioTrack(nil) + if !mediaEditor.sourceIsVideo && !mediaEditor.isPlaying { + mediaEditor.play() + } } + self.requestUpdate(transition: .easeInOut(duration: 0.25)) } - self.requestUpdate(transition: .easeInOut(duration: 0.25)) } - } + ) ) ) - ] + } let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkPresentationTheme) let contextController = ContextController(presentationData: presentationData, source: .reference(ReferenceContentSource(sourceView: sourceView, contentArea: UIScreen.main.bounds, customPosition: CGPoint(x: 0.0, y: -3.0))), items: .single(ContextController.Items(content: .list(items)))) @@ -3458,7 +3623,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } switch mode { case .sticker: - self.mediaEditor?.stop() + self.mediaEditor?.maybePauseVideo() let controller = StickerPickerScreen(context: self.context, inputData: self.stickerPickerInputData.get(), defaultToEmoji: self.defaultToEmoji, hasGifs: true) controller.completion = { [weak self] content in if let self { @@ -3487,7 +3652,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.controller?.requestLayout(transition: .immediate) } self.stickerScreen = nil - self.mediaEditor?.play() + self.mediaEditor?.maybeUnpauseVideo() } return true } @@ -3545,17 +3710,18 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.mediaEditor?.play() } } + controller.pushController = { [weak self] c in + self?.controller?.push(c) + } self.stickerScreen = controller - self.controller?.present(controller, in: .window(.root)) - return + self.controller?.present(controller, in: .current) case .text: - self.mediaEditor?.stop() + self.mediaEditor?.maybePauseVideo() self.insertTextEntity() self.hasAnyChanges = true self.controller?.isSavingAvailable = true self.controller?.requestLayout(transition: .immediate) - return case .drawing: self.previousDrawingData = self.drawingView.drawingData self.previousDrawingEntities = self.entitiesView.entities @@ -3739,6 +3905,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let previewFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - previewSize.width) / 2.0), y: topInset - bottomInputOffset + self.dismissOffset), size: previewSize) transition.setFrame(view: self.previewContainerView, frame: previewFrame) + + transition.setFrame(view: self.previewView, frame: CGRect(origin: .zero, size: previewSize)) + let entitiesViewScale = previewSize.width / storyDimensions.width self.entitiesContainerView.transform = CGAffineTransformMakeScale(entitiesViewScale, entitiesViewScale) self.entitiesContainerView.frame = CGRect(origin: .zero, size: previewFrame.size) @@ -3759,7 +3928,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } } - fileprivate var node: Node { + var node: Node { return self.displayNode as! Node } @@ -3846,7 +4015,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } } - public enum Result { + public enum MediaResult { public enum VideoResult { case imageFile(path: String) case videoFile(path: String) @@ -3856,10 +4025,20 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate case video(video: VideoResult, coverImage: UIImage?, values: MediaEditorValues, duration: Double, dimensions: PixelDimensions) } - fileprivate let context: AccountContext - fileprivate let subject: Signal - fileprivate let isEditingStory: Bool + public struct Result { + public let media: MediaResult? + public let mediaAreas: [MediaArea] + public let caption: NSAttributedString + public let options: MediaEditorResultPrivacy + public let stickers: [TelegramMediaFile] + public let randomId: Int64 + } + + let context: AccountContext + let subject: Signal + let isEditingStory: Bool fileprivate let customTarget: EnginePeer.Id? + let forwardSource: (EnginePeer, EngineStoryItem)? fileprivate let initialCaption: NSAttributedString? fileprivate let initialPrivacy: EngineStoryPrivacy? @@ -3870,7 +4049,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate fileprivate let transitionOut: (Bool, Bool?) -> TransitionOut? public var cancelled: (Bool) -> Void = { _ in } - public var completion: (Int64, MediaEditorScreen.Result?, [MediaArea], NSAttributedString, MediaEditorResultPrivacy, [TelegramMediaFile], @escaping (@escaping () -> Void) -> Void) -> Void = { _, _, _, _, _, _, _ in } + public var completion: (MediaEditorScreen.Result, @escaping (@escaping () -> Void) -> Void) -> Void = { _, _ in } public var dismissed: () -> Void = { } public var willDismiss: () -> Void = { } @@ -3880,23 +4059,29 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate private let hapticFeedback = HapticFeedback() + private var audioSessionDisposable: Disposable? + private let postingAvailabilityPromise = Promise() + private var postingAvailabilityDisposable: Disposable? + public init( context: AccountContext, subject: Signal, customTarget: EnginePeer.Id? = nil, - isEditing: Bool, + isEditing: Bool = false, + forwardSource: (EnginePeer, EngineStoryItem)? = nil, initialCaption: NSAttributedString? = nil, initialPrivacy: EngineStoryPrivacy? = nil, initialMediaAreas: [MediaArea]? = nil, initialVideoPosition: Double? = nil, transitionIn: TransitionIn?, transitionOut: @escaping (Bool, Bool?) -> TransitionOut?, - completion: @escaping (Int64, MediaEditorScreen.Result?, [MediaArea], NSAttributedString, MediaEditorResultPrivacy, [TelegramMediaFile], @escaping (@escaping () -> Void) -> Void) -> Void + completion: @escaping (MediaEditorScreen.Result, @escaping (@escaping () -> Void) -> Void) -> Void ) { self.context = context self.subject = subject self.customTarget = customTarget self.isEditingStory = isEditing + self.forwardSource = forwardSource self.initialCaption = initialCaption self.initialPrivacy = initialPrivacy self.initialMediaAreas = initialMediaAreas @@ -3948,6 +4133,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate updateStorySources(engine: self.context.engine) updateStoryDrafts(engine: self.context.engine) + + if let _ = forwardSource { + self.postingAvailabilityPromise.set(self.context.engine.messages.checkStoriesUploadAvailability(target: .myStories)) + } } required public init(coder aDecoder: NSCoder) { @@ -3956,6 +4145,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate deinit { self.exportDisposable.dispose() + self.audioSessionDisposable?.dispose() + self.postingAvailabilityDisposable?.dispose() } override public func loadDisplayNode() { @@ -3969,11 +4160,74 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate Queue.mainQueue().after(0.4) { self.adminedChannels.set(.single([]) |> then(self.context.engine.peers.channelsForStories())) self.closeFriends.set(self.context.engine.data.get(TelegramEngine.EngineData.Item.Contacts.CloseFriends())) + + if self.forwardSource != nil || self.isEditingStory { + self.audioSessionDisposable = self.context.sharedContext.mediaManager.audioSession.push(audioSessionType: .recordWithOthers, activate: { _ in + if #available(iOS 13.0, *) { + try? AVAudioSession.sharedInstance().setAllowHapticsAndSystemSoundsDuringRecording(true) + } + }, deactivate: { _ in + return .single(Void()) + }) + } + + self.postingAvailabilityDisposable = (self.postingAvailabilityPromise.get() + |> deliverOnMainQueue).start(next: { [weak self] availability in + guard let self, availability != .available else { + return + } + + let subject: PremiumLimitSubject + switch availability { + case .expiringLimit: + subject = .expiringStories + case .weeklyLimit: + subject = .storiesWeekly + case .monthlyLimit: + subject = .storiesMonthly + default: + subject = .expiringStories + } + + let context = self.context + var replaceImpl: ((ViewController) -> Void)? + let controller = self.context.sharedContext.makePremiumLimitController(context: self.context, subject: subject, count: 10, forceDark: true, cancel: { [weak self] in + self?.requestDismiss(saveDraft: false, animated: true) + }, action: { [weak self] in + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .stories, forceDark: true, dismissed: { [weak self] in + guard let self else { + return + } + 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 else { + return + } + let isPremium = peer?.isPremium ?? false + if !isPremium { + self.requestDismiss(saveDraft: false, animated: true) + } + }) + }) + replaceImpl?(controller) + return true + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + if let navigationController = self.context.sharedContext.mainWindow?.viewController as? NavigationController { + navigationController.pushViewController(controller) + } + }) } } + + fileprivate var isEmbeddedEditor: Bool { + return self.isEditingStory || self.forwardSource != nil + } func openPrivacySettings(_ privacy: MediaEditorResultPrivacy? = nil, completion: @escaping () -> Void = {}) { - self.node.mediaEditor?.stop() + self.node.mediaEditor?.maybePauseVideo() self.hapticFeedback.impact(.light) @@ -4335,25 +4589,6 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.present(controller, in: .current) } - func isEligibleForDraft() -> Bool { - if self.isEditingStory { - return false - } - guard let mediaEditor = self.node.mediaEditor else { - return false - } - let entities = self.node.entitiesView.entities.filter { !($0 is DrawingMediaEntity) } - let codableEntities = DrawingEntitiesView.encodeEntities(entities, entitiesView: self.node.entitiesView) - mediaEditor.setDrawingAndEntities(data: nil, image: mediaEditor.values.drawing, entities: codableEntities) - - let caption = self.getCaption() - - if let subject = self.node.subject, case .asset = subject, self.node.mediaEditor?.values.hasChanges == false && caption.string.isEmpty { - return false - } - return true - } - func maybePresentDiscardAlert() { self.hapticFeedback.impact(.light) if !self.isEligibleForDraft() { @@ -4371,10 +4606,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate title = presentationData.strings.Story_Editor_DraftDiscardMedia save = presentationData.strings.Story_Editor_DraftKeepMedia } - let theme = defaultDarkPresentationTheme let controller = textAlertController( context: self.context, - forceTheme: theme, + forceTheme: defaultDarkPresentationTheme, title: title, text: presentationData.strings.Story_Editor_DraftDiscaedText, actions: [ @@ -4427,115 +4661,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate }) } - private func getCaption() -> NSAttributedString { + func getCaption() -> NSAttributedString { return (self.node.componentHost.view as? MediaEditorScreenComponent.View)?.getInputText() ?? NSAttributedString() } - private func saveDraft(id: Int64?) { - guard let subject = self.node.subject, let mediaEditor = self.node.mediaEditor else { - return - } - try? FileManager.default.createDirectory(atPath: draftPath(engine: self.context.engine), withIntermediateDirectories: true) - - let values = mediaEditor.values - let privacy = self.state.privacy - let caption = self.getCaption() - let duration = mediaEditor.duration ?? 0.0 - - let currentTimestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) - var timestamp: Int32 - var location: CLLocationCoordinate2D? - let expiresOn: Int32 - if case let .draft(draft, _) = subject { - timestamp = draft.timestamp - location = draft.location - if let _ = id { - expiresOn = draft.expiresOn ?? currentTimestamp + 3600 * 24 * 7 - } else { - expiresOn = currentTimestamp + 3600 * 24 * 7 - } - } else { - timestamp = currentTimestamp - if case let .asset(asset) = subject { - location = asset.location?.coordinate - } - if let _ = id { - expiresOn = currentTimestamp + Int32(self.state.privacy.timeout) - } else { - expiresOn = currentTimestamp + 3600 * 24 * 7 - } - } - - if let resultImage = mediaEditor.resultImage { - mediaEditor.seek(0.0, andPlay: false) - makeEditorImageComposition(context: self.node.ciContext, postbox: self.context.account.postbox, inputImage: resultImage, dimensions: storyDimensions, values: values, time: .zero, textScale: 2.0, completion: { resultImage in - guard let resultImage else { - return - } - let fittedSize = resultImage.size.aspectFitted(CGSize(width: 128.0, height: 128.0)) - - let context = self.context - let saveImageDraft: (UIImage, PixelDimensions) -> Void = { image, dimensions in - if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) { - let path = "\(Int64.random(in: .min ... .max)).jpg" - if let data = image.jpegData(compressionQuality: 0.87) { - let draft = MediaEditorDraft(path: path, isVideo: false, thumbnail: thumbnailImage, dimensions: dimensions, duration: nil, values: values, caption: caption, privacy: privacy, timestamp: timestamp, location: location, expiresOn: expiresOn) - try? data.write(to: URL(fileURLWithPath: draft.fullPath(engine: context.engine))) - if let id { - saveStorySource(engine: context.engine, item: draft, peerId: context.account.peerId, id: id) - } else { - addStoryDraft(engine: context.engine, item: draft) - } - } - } - } - - let saveVideoDraft: (String, PixelDimensions, Double) -> Void = { videoPath, dimensions, duration in - if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) { - let path = "\(Int64.random(in: .min ... .max)).mp4" - let draft = MediaEditorDraft(path: path, isVideo: true, thumbnail: thumbnailImage, dimensions: dimensions, duration: duration, values: values, caption: caption, privacy: privacy, timestamp: timestamp, location: location, expiresOn: expiresOn) - try? FileManager.default.copyItem(atPath: videoPath, toPath: draft.fullPath(engine: context.engine)) - if let id { - saveStorySource(engine: context.engine, item: draft, peerId: context.account.peerId, id: id) - } else { - addStoryDraft(engine: context.engine, item: draft) - } - } - } - - switch subject { - case let .image(image, dimensions, _, _): - saveImageDraft(image, dimensions) - case let .video(path, _, _, _, _, dimensions, _, _, _): - saveVideoDraft(path, dimensions, duration) - case let .asset(asset): - if asset.mediaType == .video { - PHImageManager.default().requestAVAsset(forVideo: asset, options: nil) { avAsset, _, _ in - if let urlAsset = avAsset as? AVURLAsset { - saveVideoDraft(urlAsset.url.relativePath, PixelDimensions(width: Int32(asset.pixelWidth), height: Int32(asset.pixelHeight)), duration) - } - } - } else { - let options = PHImageRequestOptions() - options.deliveryMode = .highQualityFormat - PHImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .default, options: options) { image, _ in - if let image { - saveImageDraft(image, PixelDimensions(image.size)) - } - } - } - case let .draft(draft, _): - if draft.isVideo { - saveVideoDraft(draft.fullPath(engine: context.engine), draft.dimensions, draft.duration ?? 0.0) - } else if let image = UIImage(contentsOfFile: draft.fullPath(engine: context.engine)) { - saveImageDraft(image, draft.dimensions) - } - removeStoryDraft(engine: self.context.engine, path: draft.path, delete: false) - } - }) - } - } - fileprivate func checkCaptionLimit() -> Bool { let caption = self.getCaption() if caption.length > self.context.userLimits.maxStoryCaptionLength { @@ -4629,8 +4758,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } } - if self.isEditingStory && !(self.node.hasAnyChanges || hasEntityChanges) { - self.completion(randomId, nil, [], caption, self.state.privacy, stickers, { [weak self] finished in + if self.isEmbeddedEditor && !(self.node.hasAnyChanges || hasEntityChanges) { + self.completion(MediaEditorScreen.Result(media: nil, mediaAreas: [], caption: caption, options: self.state.privacy, stickers: stickers, randomId: randomId), { [weak self] finished in self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in self?.dismiss() Queue.mainQueue().justDispatch { @@ -4659,7 +4788,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate var firstFrame: Signal<(UIImage?, UIImage?), NoError> let firstFrameTime = CMTime(seconds: mediaEditor.values.videoTrimRange?.lowerBound ?? 0.0, preferredTimescale: CMTimeScale(60)) - let videoResult: Result.VideoResult + let videoResult: MediaResult.VideoResult var videoIsMirrored = false let duration: Double switch subject { @@ -4680,6 +4809,11 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } else { duration = durationValue } + + var additionalPath = additionalPath + if additionalPath == nil, let valuesAdditionalPath = mediaEditor.values.additionalVideoPath { + additionalPath = valuesAdditionalPath + } firstFrame = Signal<(UIImage?, UIImage?), NoError> { subscriber in let avAsset = AVURLAsset(url: URL(fileURLWithPath: path)) @@ -4721,6 +4855,12 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } else { duration = 5.0 } + + var additionalPath: String? + if let valuesAdditionalPath = mediaEditor.values.additionalVideoPath { + additionalPath = valuesAdditionalPath + } + firstFrame = Signal<(UIImage?, UIImage?), NoError> { subscriber in if asset.mediaType == .video { PHImageManager.default().requestAVAsset(forVideo: asset, options: nil) { avAsset, _, _ in @@ -4729,8 +4869,23 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate avAssetGenerator.appliesPreferredTrackTransform = true avAssetGenerator.generateCGImagesAsynchronously(forTimes: [NSValue(time: firstFrameTime)], completionHandler: { _, cgImage, _, _, _ in if let cgImage { - subscriber.putNext((UIImage(cgImage: cgImage), nil)) - subscriber.putCompletion() + if let additionalPath { + let avAsset = AVURLAsset(url: URL(fileURLWithPath: additionalPath)) + let avAssetGenerator = AVAssetImageGenerator(asset: avAsset) + avAssetGenerator.appliesPreferredTrackTransform = true + avAssetGenerator.generateCGImagesAsynchronously(forTimes: [NSValue(time: firstFrameTime)], completionHandler: { _, additionalCGImage, _, _, _ in + if let additionalCGImage { + subscriber.putNext((UIImage(cgImage: cgImage), UIImage(cgImage: additionalCGImage))) + subscriber.putCompletion() + } else { + subscriber.putNext((UIImage(cgImage: cgImage), nil)) + subscriber.putCompletion() + } + }) + } else { + subscriber.putNext((UIImage(cgImage: cgImage), nil)) + subscriber.putCompletion() + } } }) } @@ -4740,8 +4895,23 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate options.deliveryMode = .highQualityFormat PHImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .default, options: options) { image, _ in if let image { - subscriber.putNext((image, nil)) - subscriber.putCompletion() + if let additionalPath { + let avAsset = AVURLAsset(url: URL(fileURLWithPath: additionalPath)) + let avAssetGenerator = AVAssetImageGenerator(asset: avAsset) + avAssetGenerator.appliesPreferredTrackTransform = true + avAssetGenerator.generateCGImagesAsynchronously(forTimes: [NSValue(time: firstFrameTime)], completionHandler: { _, additionalCGImage, _, _, _ in + if let additionalCGImage { + subscriber.putNext((image, UIImage(cgImage: additionalCGImage))) + subscriber.putCompletion() + } else { + subscriber.putNext((image, nil)) + subscriber.putCompletion() + } + }) + } else { + subscriber.putNext((image, nil)) + subscriber.putCompletion() + } } } } @@ -4787,7 +4957,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if let self { var currentImage = mediaEditor.resultImage if let image { - mediaEditor.replaceSource(image, additionalImage: additionalImage, time: firstFrameTime) + mediaEditor.replaceSource(image, additionalImage: additionalImage, time: firstFrameTime, mirror: true) if let updatedImage = mediaEditor.getResultImage(mirror: videoIsMirrored) { currentImage = updatedImage } @@ -4805,7 +4975,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate makeEditorImageComposition(context: self.node.ciContext, postbox: self.context.account.postbox, inputImage: inputImage, dimensions: storyDimensions, values: mediaEditor.values, time: firstFrameTime, textScale: 2.0, completion: { [weak self] coverImage in if let self { Logger.shared.log("MediaEditor", "Completed with video \(videoResult)") - self.completion(randomId, .video(video: videoResult, coverImage: coverImage, values: mediaEditor.values, duration: duration, dimensions: mediaEditor.values.resultDimensions), mediaAreas, caption, self.state.privacy, stickers, { [weak self] finished in + self.completion(MediaEditorScreen.Result(media: .video(video: videoResult, coverImage: coverImage, values: mediaEditor.values, duration: duration, dimensions: mediaEditor.values.resultDimensions), mediaAreas: mediaAreas, caption: caption, options: self.state.privacy, stickers: stickers, randomId: randomId), { [weak self] finished in self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in self?.dismiss() Queue.mainQueue().justDispatch { @@ -4828,7 +4998,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate makeEditorImageComposition(context: self.node.ciContext, postbox: self.context.account.postbox, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, textScale: 2.0, completion: { [weak self] resultImage in if let self, let resultImage { Logger.shared.log("MediaEditor", "Completed with image \(resultImage)") - self.completion(randomId, .image(image: resultImage, dimensions: PixelDimensions(resultImage.size)), mediaAreas, caption, self.state.privacy, stickers, { [weak self] finished in + self.completion(MediaEditorScreen.Result(media: .image(image: resultImage, dimensions: PixelDimensions(resultImage.size)), mediaAreas: mediaAreas, caption: caption, options: self.state.privacy, stickers: stickers, randomId: randomId), { [weak self] finished in self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in self?.dismiss() Queue.mainQueue().justDispatch { @@ -4901,7 +5071,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } if mediaEditor.resultIsVideo { - mediaEditor.stop() + mediaEditor.maybePauseVideo() self.node.entitiesView.pause() let exportSubject: Signal @@ -4969,16 +5139,18 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate saveToPhotos(outputPath, true) self.node.presentSaveTooltip() - self.node.mediaEditor?.play() - self.node.entitiesView.play() + if let mediaEditor = self.node.mediaEditor, mediaEditor.maybeUnpauseVideo() { + self.node.entitiesView.play() + } case let .progress(progress): if self.videoExport != nil { self.node.updateVideoExportProgress(progress) } case .failed: self.videoExport = nil - self.node.mediaEditor?.play() - self.node.entitiesView.play() + if let mediaEditor = self.node.mediaEditor, mediaEditor.maybeUnpauseVideo() { + self.node.entitiesView.play() + } case .unknown: break } @@ -5255,10 +5427,7 @@ private final class ToolValueComponent: Component { ) if let titleView = self.title.view { if titleView.superview == nil { - titleView.layer.shadowOffset = CGSize(width: 0.0, height: 0.0) - titleView.layer.shadowRadius = 3.0 - titleView.layer.shadowColor = UIColor.black.cgColor - titleView.layer.shadowOpacity = 0.35 + setupButtonShadow(titleView, radius: 3.0) self.addSubview(titleView) } transition.setPosition(view: titleView, position: titleFrame.center) @@ -5281,10 +5450,7 @@ private final class ToolValueComponent: Component { ) if let valueView = self.value.view { if valueView.superview == nil { - valueView.layer.shadowOffset = CGSize(width: 0.0, height: 0.0) - valueView.layer.shadowRadius = 3.0 - valueView.layer.shadowColor = UIColor.black.cgColor - valueView.layer.shadowOpacity = 0.35 + setupButtonShadow(valueView, radius: 3.0) self.addSubview(valueView) } transition.setPosition(view: valueView, position: valueFrame.center) @@ -5551,3 +5717,10 @@ private final class ReferenceContentSource: ContextReferenceContentSource { return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: self.contentArea, customPosition: self.customPosition, actionsPosition: .top) } } + +private func setupButtonShadow(_ view: UIView, radius: CGFloat = 2.0) { + view.layer.shadowOffset = CGSize(width: 0.0, height: 0.0) + view.layer.shadowRadius = radius + view.layer.shadowColor = UIColor.black.cgColor + view.layer.shadowOpacity = 0.35 +} diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaScrubberComponent.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaScrubberComponent.swift new file mode 100644 index 00000000000..037d7fa5441 --- /dev/null +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaScrubberComponent.swift @@ -0,0 +1,1386 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import ComponentFlow +import SwiftSignalKit +import ViewControllerComponent +import ComponentDisplayAdapters +import TelegramPresentationData +import AccountContext +import AudioWaveformComponent +import MultilineTextComponent +import MediaEditor + +private let handleWidth: CGFloat = 14.0 +private let trackHeight: CGFloat = 39.0 +private let collapsedTrackHeight: CGFloat = 26.0 +private let trackSpacing: CGFloat = 4.0 +private let borderHeight: CGFloat = 1.0 + UIScreenPixel +private let frameWidth: CGFloat = 24.0 + +final class MediaScrubberComponent: Component { + typealias EnvironmentType = Empty + + struct Track: Equatable { + enum Content: Equatable { + case video(frames: [UIImage], framesUpdateTimestamp: Double) + case audio(artist: String?, title: String?, samples: Data?, peak: Int32) + + static func ==(lhs: Content, rhs: Content) -> Bool { + switch lhs { + case let .video(_, framesUpdateTimestamp): + if case .video(_, framesUpdateTimestamp) = rhs { + return true + } else { + return false + } + case let .audio(lhsArtist, lhsTitle, lhsSamples, lhsPeak): + if case let .audio(rhsArtist, rhsTitle, rhsSamples, rhsPeak) = rhs { + return lhsArtist == rhsArtist && lhsTitle == rhsTitle && lhsSamples == rhsSamples && lhsPeak == rhsPeak + } else { + return false + } + } + } + } + + let id: Int32 + let content: Content + let duration: Double + let trimRange: Range? + let offset: Double? + let isMain: Bool + + init(_ track: MediaEditorPlayerState.Track) { + self.id = track.id + switch track.content { + case let .video(frames, framesUpdateTimestamp): + self.content = .video(frames: frames, framesUpdateTimestamp: framesUpdateTimestamp) + case let .audio(artist, title, samples, peak): + self.content = .audio(artist: artist, title: title, samples: samples, peak: peak) + } + self.duration = track.duration + self.trimRange = track.trimRange + self.offset = track.offset + self.isMain = track.isMain + } + } + + let context: AccountContext + let generationTimestamp: Double + + let position: Double + let minDuration: Double + let maxDuration: Double + let isPlaying: Bool + + let tracks: [Track] + + let positionUpdated: (Double, Bool) -> Void + let trackTrimUpdated: (Int32, Double, Double, Bool, Bool) -> Void + let trackOffsetUpdated: (Int32, Double, Bool) -> Void + let trackLongPressed: (Int32, UIView) -> Void + + init( + context: AccountContext, + generationTimestamp: Double, + position: Double, + minDuration: Double, + maxDuration: Double, + isPlaying: Bool, + tracks: [Track], + positionUpdated: @escaping (Double, Bool) -> Void, + trackTrimUpdated: @escaping (Int32, Double, Double, Bool, Bool) -> Void, + trackOffsetUpdated: @escaping (Int32, Double, Bool) -> Void, + trackLongPressed: @escaping (Int32, UIView) -> Void + ) { + self.context = context + self.generationTimestamp = generationTimestamp + self.position = position + self.minDuration = minDuration + self.maxDuration = maxDuration + self.isPlaying = isPlaying + self.tracks = tracks + self.positionUpdated = positionUpdated + self.trackTrimUpdated = trackTrimUpdated + self.trackOffsetUpdated = trackOffsetUpdated + self.trackLongPressed = trackLongPressed + } + + static func ==(lhs: MediaScrubberComponent, rhs: MediaScrubberComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.generationTimestamp != rhs.generationTimestamp { + return false + } + if lhs.position != rhs.position { + return false + } + if lhs.minDuration != rhs.minDuration { + return false + } + if lhs.maxDuration != rhs.maxDuration { + return false + } + if lhs.isPlaying != rhs.isPlaying { + return false + } + if lhs.tracks != rhs.tracks { + return false + } + return true + } + + final class View: UIView, UIGestureRecognizerDelegate { + private var trackViews: [Int32: TrackView] = [:] + private let trimView: TrimView + private let ghostTrimView: TrimView + private let cursorView: HandleView + + private var cursorDisplayLink: SharedDisplayLinkDriver.Link? + private var cursorPositionAnimation: (start: Double, from: Double, to: Double, ended: Bool)? + + private var selectedTrackId: Int32 = 0 + private var isPanningCursor = false + + private var scrubberSize: CGSize? + + private var component: MediaScrubberComponent? + private weak var state: EmptyComponentState? + + override init(frame: CGRect) { + self.trimView = TrimView(frame: .zero) + self.ghostTrimView = TrimView(frame: .zero) + self.ghostTrimView.isHollow = true + self.cursorView = HandleView() + + super.init(frame: frame) + + self.clipsToBounds = false + + self.disablesInteractiveModalDismiss = true + self.disablesInteractiveKeyboardGestureRecognizer = true + + let positionImage = generateImage(CGSize(width: handleWidth, height: 50.0), rotatedContext: { size, context in + context.clear(CGRect(origin: .zero, size: size)) + context.setFillColor(UIColor.white.cgColor) + context.setShadow(offset: .zero, blur: 2.0, color: UIColor(rgb: 0x000000, alpha: 0.55).cgColor) + + let path = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: 6.0, y: 4.0), size: CGSize(width: 2.0, height: 42.0)), cornerRadius: 1.0) + context.addPath(path.cgPath) + context.fillPath() + })?.stretchableImage(withLeftCapWidth: Int(handleWidth / 2.0), topCapHeight: 25) + + self.cursorView.image = positionImage + self.cursorView.isUserInteractionEnabled = true + self.cursorView.hitTestSlop = UIEdgeInsets(top: -8.0, left: -9.0, bottom: -8.0, right: -9.0) + + self.addSubview(self.ghostTrimView) + self.addSubview(self.trimView) + self.addSubview(self.cursorView) + + self.cursorView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handleCursorPan(_:)))) + + self.cursorDisplayLink = SharedDisplayLinkDriver.shared.add { [weak self] _ in + self?.updateCursorPosition() + } + self.cursorDisplayLink?.isPaused = true + + self.trimView.updated = { [weak self] transition in + self?.state?.updated(transition: transition) + } + self.trimView.trimUpdated = { [weak self] startValue, endValue, updatedEnd, done in + if let self, let component = self.component { + component.trackTrimUpdated(self.selectedTrackId, startValue, endValue, updatedEnd, done) + } + } + self.ghostTrimView.trimUpdated = { [weak self] startValue, endValue, updatedEnd, done in + if let self, let component = self.component { + component.trackTrimUpdated(0, startValue, endValue, updatedEnd, done) + } + } + + let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(self.longPressed(_:))) + longPressGesture.delegate = self + self.addGestureRecognizer(longPressGesture) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.cursorDisplayLink?.invalidate() + } + + private var isAudioOnly: Bool { + guard let component = self.component else { + return false + } + var hasVideoTracks = false + var hasAudioTracks = false + for track in component.tracks { + switch track.content { + case .video: + hasVideoTracks = true + case .audio: + hasAudioTracks = true + } + } + return !hasVideoTracks && hasAudioTracks + } + + private var trimDuration: Double { + guard let component = self.component, var duration = component.tracks.first?.duration else { + return 0.0 + } + if self.isAudioOnly { + duration = min(30.0, duration) + } + return duration + } + + private var duration: Double { + guard let component = self.component, let firstTrack = component.tracks.first else { + return 0.0 + } + return max(0.0, firstTrack.duration) + } + + private var startPosition: Double { + guard let component = self.component, let firstTrack = component.tracks.first else { + return 0.0 + } + return max(0.0, firstTrack.trimRange?.lowerBound ?? 0.0) + } + + private var endPosition: Double { + guard let component = self.component, let firstTrack = component.tracks.first else { + return 0.0 + } + return firstTrack.trimRange?.upperBound ?? min(firstTrack.duration, storyMaxVideoDuration) + } + + private var mainAudioTrackOffset: Double? { + guard self.isAudioOnly, let component = self.component, let firstTrack = component.tracks.first else { + return nil + } + return firstTrack.offset + } + + @objc private func longPressed(_ gestureRecognizer: UILongPressGestureRecognizer) { + guard let component = self.component, case .began = gestureRecognizer.state else { + return + } + let point = gestureRecognizer.location(in: self) + for (id, trackView) in self.trackViews { + if trackView.frame.contains(point) { + component.trackLongPressed(id, trackView.clippingView) + return + } + } + } + + @objc private func handleCursorPan(_ gestureRecognizer: UIPanGestureRecognizer) { + guard let component = self.component else { + return + } + + let location = gestureRecognizer.location(in: self) + let start = handleWidth + let end = self.frame.width - handleWidth + let length = end - start + let fraction = (location.x - start) / length + + var position = max(self.startPosition, min(self.endPosition, self.trimDuration * fraction)) + if let offset = self.mainAudioTrackOffset { + position += offset + } + let transition: Transition = .immediate + switch gestureRecognizer.state { + case .began, .changed: + self.isPanningCursor = true + component.positionUpdated(position, false) + case .ended, .cancelled: + self.isPanningCursor = false + component.positionUpdated(position, true) + default: + break + } + self.state?.updated(transition: transition) + } + + private func cursorFrame(size: CGSize, height: CGFloat, position: Double, duration : Double) -> CGRect { + let cursorPadding: CGFloat = 8.0 + let cursorPositionFraction = duration > 0.0 ? position / duration : 0.0 + let cursorPosition = floorToScreenPixels(handleWidth - 1.0 + (size.width - handleWidth * 2.0 + 2.0) * cursorPositionFraction) + var cursorFrame = CGRect(origin: CGPoint(x: cursorPosition - handleWidth / 2.0, y: -5.0 - UIScreenPixel), size: CGSize(width: handleWidth, height: height)) + + var leftEdge = self.ghostTrimView.leftHandleView.frame.maxX + var rightEdge = self.ghostTrimView.rightHandleView.frame.minX + if self.isAudioOnly { + leftEdge = self.trimView.leftHandleView.frame.maxX + rightEdge = self.trimView.rightHandleView.frame.minX + } + + cursorFrame.origin.x = max(leftEdge - cursorPadding, cursorFrame.origin.x) + cursorFrame.origin.x = min(rightEdge - handleWidth + cursorPadding, cursorFrame.origin.x) + return cursorFrame + } + + private var effectiveCursorHeight: CGFloat { + let additionalTracksCount = max(0, (self.component?.tracks.count ?? 1) - 1) + return 50.0 + CGFloat(additionalTracksCount) * 30.0 + } + + private func updateCursorPosition() { + guard let component = self.component, let scrubberSize = self.scrubberSize else { + return + } + let timestamp = CACurrentMediaTime() + + let updatedPosition: Double + if let (start, from, to, _) = self.cursorPositionAnimation { + var from = from + if let offset = self.mainAudioTrackOffset { + from -= offset + } + let duration = to - from + let fraction = duration > 0.0 ? (timestamp - start) / duration : 0.0 + updatedPosition = max(self.startPosition, min(self.endPosition, from + (to - from) * fraction)) + if fraction >= 1.0 { + self.cursorPositionAnimation = (start, from, to, true) + } + } else { + var position = component.position + if let offset = self.mainAudioTrackOffset { + position -= offset + } + let advance = component.isPlaying ? timestamp - component.generationTimestamp : 0.0 + updatedPosition = max(self.startPosition, min(self.endPosition, position + advance)) + } + self.cursorView.frame = cursorFrame(size: scrubberSize, height: self.effectiveCursorHeight, position: updatedPosition, duration: self.trimDuration) + } + + func update(component: MediaScrubberComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + let isFirstTime = self.component == nil + self.component = component + self.state = state + + var totalHeight: CGFloat = 0.0 + var trackLayout: [Int32: (CGRect, Transition, Bool)] = [:] + + if !component.tracks.contains(where: { $0.id == self.selectedTrackId }) { + self.selectedTrackId = component.tracks.first(where: { $0.isMain })?.id ?? 0 + } + + var lowestVideoId: Int32? + + var validIds = Set() + for track in component.tracks { + let id = track.id + validIds.insert(id) + + if case .video = track.content { + if lowestVideoId == nil { + lowestVideoId = track.id + } + } + + var trackTransition = transition + let trackView: TrackView + var animateTrackIn = false + if let current = self.trackViews[id] { + trackView = current + } else { + trackTransition = .immediate + trackView = TrackView() + trackView.onSelection = { [weak self] id in + guard let self else { + return + } + self.selectedTrackId = id + self.state?.updated(transition: .easeInOut(duration: 0.2)) + } + trackView.offsetUpdated = { [weak self] offset, apply in + guard let self, let component = self.component else { + return + } + component.trackOffsetUpdated(id, offset, apply) + } + trackView.updated = { [weak self] transition in + guard let self else { + return + } + self.state?.updated(transition: transition) + } + self.trackViews[id] = trackView + + self.insertSubview(trackView, at: 0) + + if !isFirstTime { + animateTrackIn = true + } + } + + let trackSize = trackView.update( + context: component.context, + track: track, + isSelected: id == self.selectedTrackId, + availableSize: availableSize, + duration: self.duration, + transition: trackTransition + ) + trackLayout[id] = (CGRect(origin: CGPoint(x: 0.0, y: totalHeight), size: trackSize), trackTransition, animateTrackIn) + + totalHeight += trackSize.height + totalHeight += trackSpacing + } + totalHeight -= trackSpacing + + for track in component.tracks { + guard let trackView = self.trackViews[track.id], let (trackFrame, trackTransition, animateTrackIn) = trackLayout[track.id] else { + continue + } + trackTransition.setFrame(view: trackView, frame: CGRect(origin: CGPoint(x: 0.0, y: totalHeight - trackFrame.maxY), size: trackFrame.size)) + if animateTrackIn { + trackView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + trackView.layer.animatePosition(from: CGPoint(x: 0.0, y: trackFrame.height + trackSpacing), to: .zero, duration: 0.35, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + } + } + + var removeIds: [Int32] = [] + for (id, trackView) in self.trackViews { + if !validIds.contains(id) { + removeIds.append(id) + trackView.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: trackView.frame.height), duration: 0.35, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) + transition.setAlpha(view: trackView, alpha: 0.0, completion: { [weak trackView] _ in + trackView?.removeFromSuperview() + }) + } + } + for id in removeIds { + self.trackViews.removeValue(forKey: id) + } + + var startPosition = self.startPosition + var endPosition = self.endPosition + var trimViewOffset: CGFloat = 0.0 + var trimViewVisualInsets: UIEdgeInsets = .zero + var trackViewWidth: CGFloat = availableSize.width + var mainTrimDuration = self.trimDuration + + if let track = component.tracks.first(where: { $0.id == self.selectedTrackId }), track.id != 0 { + if let trimRange = track.trimRange { + startPosition = trimRange.lowerBound + endPosition = trimRange.upperBound + } + if let trackView = self.trackViews[track.id] { + if trackView.scrollView.contentOffset.x < 0.0 { + trimViewOffset = -trackView.scrollView.contentOffset.x + trimViewVisualInsets.right = trimViewOffset + } else if trackView.scrollView.contentSize.width > trackView.scrollView.frame.width, trackView.scrollView.contentOffset.x > trackView.scrollView.contentSize.width - trackView.scrollView.frame.width { + let delta = trackView.scrollView.contentOffset.x - (trackView.scrollView.contentSize.width - trackView.scrollView.frame.width) + trimViewOffset = -delta + trimViewVisualInsets.left = delta + } + + if lowestVideoId == 0 && track.id == 1 { + trimViewVisualInsets = .zero + trackViewWidth = trackView.containerView.frame.width + mainTrimDuration = track.duration + } + } + } + + let scrubberSize = CGSize(width: availableSize.width, height: trackHeight) + + self.trimView.isHollow = self.selectedTrackId != lowestVideoId || self.isAudioOnly + let (leftHandleFrame, rightHandleFrame) = self.trimView.update( + visualInsets: trimViewVisualInsets, + scrubberSize: CGSize(width: trackViewWidth, height: trackHeight), + duration: mainTrimDuration, + startPosition: startPosition, + endPosition: endPosition, + position: component.position, + minDuration: component.minDuration, + maxDuration: component.maxDuration, + transition: transition + ) + + let (ghostLeftHandleFrame, ghostRightHandleFrame) = self.ghostTrimView.update( + visualInsets: .zero, + scrubberSize: CGSize(width: scrubberSize.width, height: collapsedTrackHeight), + duration: self.duration, + startPosition: self.startPosition, + endPosition: self.endPosition, + position: component.position, + minDuration: component.minDuration, + maxDuration: component.maxDuration, + transition: transition + ) + + let _ = leftHandleFrame + let _ = rightHandleFrame + let _ = ghostLeftHandleFrame + let _ = ghostRightHandleFrame + + let scrubberBounds = CGRect(origin: .zero, size: scrubberSize) + var selectedTrackFrame = scrubberBounds + var mainTrackFrame = scrubberBounds + if let (trackFrame, _, _) = trackLayout[0] { + mainTrackFrame = CGRect(origin: CGPoint(x: trackFrame.minX, y: totalHeight - trackFrame.maxY), size: trackFrame.size) + } + if let (trackFrame, _, _) = trackLayout[self.selectedTrackId] { + selectedTrackFrame = CGRect(origin: CGPoint(x: trackFrame.minX, y: totalHeight - trackFrame.maxY), size: trackFrame.size) + } else { + selectedTrackFrame = mainTrackFrame + } + + let trimViewFrame = CGRect(origin: CGPoint(x: trimViewOffset, y: selectedTrackFrame.minY), size: scrubberSize) + transition.setFrame(view: self.trimView, frame: trimViewFrame) + + var ghostTrimVisible = false + if let lowestVideoId, self.selectedTrackId != lowestVideoId { + ghostTrimVisible = true + } + + let ghostTrimViewFrame = CGRect(origin: CGPoint(x: 0.0, y: totalHeight - collapsedTrackHeight), size: CGSize(width: availableSize.width, height: collapsedTrackHeight)) + transition.setFrame(view: self.ghostTrimView, frame: ghostTrimViewFrame) + transition.setAlpha(view: self.ghostTrimView, alpha: ghostTrimVisible ? 0.75 : 0.0) + +// var containerLeftEdge = leftHandleFrame.maxX +// var containerRightEdge = rightHandleFrame.minX +// if self.isAudioSelected && component.duration > 0.0 { +// containerLeftEdge = ghostLeftHandleFrame.maxX +// containerRightEdge = ghostRightHandleFrame.minX +// } + + let isDraggingTracks = self.trackViews.values.contains(where: { $0.isDragging }) + let isCursorHidden = isDraggingTracks || self.trimView.isPanningTrimHandle || self.ghostTrimView.isPanningTrimHandle + var cursorTransition = transition + if isCursorHidden { + cursorTransition = .immediate + } + cursorTransition.setAlpha(view: self.cursorView, alpha: isCursorHidden ? 0.0 : 1.0, delay: self.cursorView.alpha.isZero && !isCursorHidden ? 0.25 : 0.0) + + self.scrubberSize = scrubberSize + if self.isPanningCursor || !component.isPlaying { + self.cursorPositionAnimation = nil + self.cursorDisplayLink?.isPaused = true + + var cursorPosition = component.position + if let offset = self.mainAudioTrackOffset { + cursorPosition -= offset + } + transition.setFrame(view: self.cursorView, frame: cursorFrame(size: scrubberSize, height: self.effectiveCursorHeight, position: cursorPosition, duration: trimDuration)) + } else { + if let (_, _, end, ended) = self.cursorPositionAnimation { + if ended, component.position >= self.startPosition && component.position < end - 1.0 { + self.cursorPositionAnimation = (CACurrentMediaTime(), component.position, self.endPosition, false) + } + } else { + self.cursorPositionAnimation = (CACurrentMediaTime(), component.position, self.endPosition, false) + } + self.cursorDisplayLink?.isPaused = false + self.updateCursorPosition() + } + + return CGSize(width: availableSize.width, height: totalHeight) + } + + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + let hitTestSlop = UIEdgeInsets(top: -8.0, left: -9.0, bottom: -8.0, right: -9.0) + return self.bounds.inset(by: hitTestSlop).contains(point) + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: State, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + + +private class TrackView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelegate { + fileprivate let clippingView: UIView + fileprivate let scrollView: UIScrollView + fileprivate let containerView: UIView + fileprivate let backgroundView: BlurredBackgroundView + fileprivate let vibrancyView: UIVisualEffectView + fileprivate let vibrancyContainer: UIView + + fileprivate let audioContentContainerView: UIView + fileprivate let audioWaveform = ComponentView() + fileprivate let waveformCloneLayer = AudioWaveformComponent.View.CloneLayer() + fileprivate let audioContentMaskView: UIImageView + fileprivate let audioIconView: UIImageView + fileprivate let audioTitle = ComponentView() + + fileprivate let videoTransparentFramesContainer = UIView() + fileprivate var videoTransparentFrameLayers: [VideoFrameLayer] = [] + fileprivate let videoOpaqueFramesContainer = UIView() + fileprivate var videoOpaqueFrameLayers: [VideoFrameLayer] = [] + + var onSelection: (Int32) -> Void = { _ in } + var offsetUpdated: (Double, Bool) -> Void = { _, _ in } + var updated: (Transition) -> Void = { _ in } + + private(set) var isDragging = false + private var ignoreScrollUpdates = false + + override init(frame: CGRect) { + self.scrollView = UIScrollView() + if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { + self.scrollView.contentInsetAdjustmentBehavior = .never + } + if #available(iOS 13.0, *) { + self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false + } + self.scrollView.bounces = false + self.scrollView.decelerationRate = .fast + self.scrollView.clipsToBounds = false + self.scrollView.showsHorizontalScrollIndicator = false + self.scrollView.showsVerticalScrollIndicator = false + + self.clippingView = UIView() + self.clippingView.clipsToBounds = true + + self.containerView = UIView() + self.containerView.clipsToBounds = true + self.containerView.layer.cornerRadius = 9.0 + self.containerView.isUserInteractionEnabled = false + + self.backgroundView = BlurredBackgroundView(color: UIColor(white: 0.0, alpha: 0.5), enableBlur: true) + + let style: UIBlurEffect.Style = .dark + let blurEffect = UIBlurEffect(style: style) + let vibrancyEffect = UIVibrancyEffect(blurEffect: blurEffect) + let vibrancyEffectView = UIVisualEffectView(effect: vibrancyEffect) + self.vibrancyView = vibrancyEffectView + + self.vibrancyContainer = UIView() + self.vibrancyView.contentView.addSubview(self.vibrancyContainer) + + self.audioContentContainerView = UIView() + self.audioContentContainerView.clipsToBounds = true + + self.audioContentMaskView = UIImageView() + self.audioContentContainerView.mask = self.audioContentMaskView + + self.audioIconView = UIImageView(image: UIImage(bundleImageName: "Media Editor/SmallAudio")) + + self.waveformCloneLayer.opacity = 0.3 + + super.init(frame: .zero) + + self.scrollView.delegate = self + + self.videoTransparentFramesContainer.alpha = 0.5 + self.videoTransparentFramesContainer.clipsToBounds = true + self.videoTransparentFramesContainer.layer.cornerRadius = 9.0 + self.videoTransparentFramesContainer.isUserInteractionEnabled = false + + self.videoOpaqueFramesContainer.clipsToBounds = true + self.videoOpaqueFramesContainer.layer.cornerRadius = 9.0 + self.videoOpaqueFramesContainer.isUserInteractionEnabled = false + + self.addSubview(self.clippingView) + self.clippingView.addSubview(self.scrollView) + self.scrollView.addSubview(self.containerView) + self.backgroundView.addSubview(self.vibrancyView) + + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:))) + self.addGestureRecognizer(tapGesture) + + self.audioContentMaskView.image = audioContentMaskImage + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func handleTap(_ gestureRecognizer: UITapGestureRecognizer) { + guard let (track, _, _) = self.params else { + return + } + self.onSelection(track.id) + } + + private func updateTrackOffset(done: Bool) { + guard self.scrollView.contentSize.width > 0.0, let duration = self.params?.track.duration else { + return + } + let totalWidth = self.scrollView.contentSize.width + let offset = self.scrollView.contentOffset.x * duration / totalWidth + self.offsetUpdated(offset, done) + } + + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + self.isDragging = true + self.updated(.easeInOut(duration: 0.25)) + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + guard !self.ignoreScrollUpdates else { + return + } + self.updateTrackOffset(done: false) + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + if !decelerate { + self.updateTrackOffset(done: true) + self.isDragging = false + self.updated(.easeInOut(duration: 0.25)) + } + } + + func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + self.updateTrackOffset(done: true) + self.isDragging = false + self.updated(.easeInOut(duration: 0.25)) + } + + override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + let location = gestureRecognizer.location(in: self.containerView) + return self.containerView.bounds.contains(location) + } + + private var params: ( + track: MediaScrubberComponent.Track, + isSelected: Bool, + duration: Double + )? + + func update( + context: AccountContext, + track: MediaScrubberComponent.Track, + isSelected: Bool, + availableSize: CGSize, + duration: Double, + transition: Transition + ) -> CGSize { + let previousParams = self.params + self.params = (track, isSelected, duration) + + let scrubberSize = CGSize(width: availableSize.width, height: isSelected ? trackHeight : collapsedTrackHeight) + + var screenSpanDuration = duration + if track.isAudio && track.isMain { + screenSpanDuration = min(30.0, track.duration) + } + + let minimalAudioWidth = handleWidth * 2.0 + var containerTotalWidth = scrubberSize.width + if track.isAudio || !track.isMain, screenSpanDuration > 0.0 { + let trackFraction = track.duration / screenSpanDuration + if trackFraction < 1.0 - .ulpOfOne || trackFraction > 1.0 + .ulpOfOne { + containerTotalWidth = max(minimalAudioWidth, ceil(availableSize.width * trackFraction)) + } + } + + var clipOrigin: CGFloat = -9.0 + var clipWidth = availableSize.width + 18.0 + + var deselectedClipWidth: CGFloat = 0.0 + var deselectedClipOrigin: CGFloat = 0.0 + + if !track.isMain, duration > 0.0 { + let trackDuration: Double + if let trimRange = track.trimRange { + trackDuration = trimRange.upperBound - trimRange.lowerBound + } else { + trackDuration = duration + } + + let fraction = trackDuration / duration + deselectedClipWidth = max(minimalAudioWidth, availableSize.width * fraction) + deselectedClipOrigin = (track.trimRange?.lowerBound ?? 0.0) / duration * availableSize.width + + if self.scrollView.contentOffset.x < 0.0 { + deselectedClipOrigin -= self.scrollView.contentOffset.x + if self.scrollView.contentSize.width > self.scrollView.frame.width { + deselectedClipWidth += self.scrollView.contentOffset.x + } + } else if self.scrollView.contentSize.width > self.scrollView.frame.width, self.scrollView.contentOffset.x > self.scrollView.contentSize.width - self.scrollView.frame.width { + let delta = self.scrollView.contentOffset.x - (self.scrollView.contentSize.width - self.scrollView.frame.width) + deselectedClipWidth -= delta + } + } + + if !isSelected && (track.isAudio || !track.isMain) { + clipOrigin = deselectedClipOrigin + clipWidth = deselectedClipWidth + } + + let clippingFrame = CGRect(origin: CGPoint(x: clipOrigin, y: 0.0), size: CGSize(width: clipWidth, height: scrubberSize.height)) + let clippingBounds = CGRect(origin: CGPoint(x: clipOrigin, y: 0.0), size: CGSize(width: clipWidth, height: scrubberSize.height)) + transition.setFrame(view: self.clippingView, frame: clippingFrame) + transition.setBounds(view: self.clippingView, bounds: clippingBounds) + + self.scrollView.isUserInteractionEnabled = isSelected && (track.isAudio || !track.isMain) + + self.ignoreScrollUpdates = true + + let scrollFrame = CGRect(origin: .zero, size: CGSize(width: availableSize.width, height: scrubberSize.height)) + transition.setFrame(view: self.scrollView, frame: scrollFrame) + + let audioChanged = !"".isEmpty + + let contentSize = CGSize(width: containerTotalWidth, height: collapsedTrackHeight) + if self.scrollView.contentSize != contentSize || audioChanged { + self.scrollView.contentSize = contentSize + if !track.isMain { + let leftInset = scrubberSize.width - handleWidth * 2.5 + let rightInset: CGFloat + if self.scrollView.contentSize.width > self.scrollView.frame.width { + rightInset = scrubberSize.width - handleWidth * 2.5 + } else { + rightInset = self.scrollView.frame.width - self.scrollView.contentSize.width + } + self.scrollView.contentInset = UIEdgeInsets(top: 0.0, left: leftInset, bottom: 0.0, right: rightInset) + } + + if let offset = track.offset, track.duration > 0.0 { + let contentOffset = offset * containerTotalWidth / track.duration + self.scrollView.contentOffset = CGPoint(x: contentOffset, y: 0.0) + } else { + self.scrollView.contentOffset = .zero + } + } + + self.ignoreScrollUpdates = false + + transition.setCornerRadius(layer: self.clippingView.layer, cornerRadius: isSelected ? 0.0 : 9.0) + + let containerFrame = CGRect(origin: .zero, size: CGSize(width: containerTotalWidth, height: scrubberSize.height)) + transition.setFrame(view: self.containerView, frame: containerFrame) + + transition.setFrame(view: self.backgroundView, frame: CGRect(origin: .zero, size: containerFrame.size)) + self.backgroundView.update(size: containerFrame.size, transition: transition.containedViewLayoutTransition) + transition.setFrame(view: self.vibrancyView, frame: CGRect(origin: .zero, size: containerFrame.size)) + transition.setFrame(view: self.vibrancyContainer, frame: CGRect(origin: .zero, size: containerFrame.size)) + + let contentContainerFrame = CGRect(origin: .zero, size: CGSize(width: clipWidth, height: containerFrame.height)) + let contentContainerOrigin = deselectedClipOrigin + self.scrollView.contentOffset.x + transition.setFrame(view: self.audioContentContainerView, frame: contentContainerFrame.offsetBy(dx: contentContainerOrigin, dy: 0.0)) + transition.setFrame(view: self.audioContentMaskView, frame: CGRect(origin: .zero, size: contentContainerFrame.size)) + + switch track.content { + case let .video(frames, framesUpdateTimestamp): + if self.videoTransparentFramesContainer.superview == nil { + self.containerView.addSubview(self.videoTransparentFramesContainer) + self.containerView.addSubview(self.videoOpaqueFramesContainer) + } + var previousFramesUpdateTimestamp: Double? + if let previousParams, case let .video(_, previousFramesUpdateTimestampValue) = previousParams.track.content { + previousFramesUpdateTimestamp = previousFramesUpdateTimestampValue + } + + if framesUpdateTimestamp != previousFramesUpdateTimestamp { + for i in 0 ..< frames.count { + let transparentFrameLayer: VideoFrameLayer + let opaqueFrameLayer: VideoFrameLayer + if i >= self.videoTransparentFrameLayers.count { + transparentFrameLayer = VideoFrameLayer() + transparentFrameLayer.masksToBounds = true + transparentFrameLayer.contentsGravity = .resizeAspectFill + self.videoTransparentFramesContainer.layer.addSublayer(transparentFrameLayer) + self.videoTransparentFrameLayers.append(transparentFrameLayer) + opaqueFrameLayer = VideoFrameLayer() + opaqueFrameLayer.masksToBounds = true + opaqueFrameLayer.contentsGravity = .resizeAspectFill + self.videoOpaqueFramesContainer.layer.addSublayer(opaqueFrameLayer) + self.videoOpaqueFrameLayers.append(opaqueFrameLayer) + } else { + transparentFrameLayer = self.videoTransparentFrameLayers[i] + opaqueFrameLayer = self.videoOpaqueFrameLayers[i] + } + transparentFrameLayer.contents = frames[i].cgImage + if let contents = opaqueFrameLayer.contents, (contents as! CGImage) !== frames[i].cgImage, opaqueFrameLayer.animation(forKey: "contents") == nil { + opaqueFrameLayer.contents = frames[i].cgImage + opaqueFrameLayer.animate(from: contents as AnyObject, to: frames[i].cgImage! as AnyObject, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 0.2) + } else { + opaqueFrameLayer.contents = frames[i].cgImage + } + if frames[i].imageOrientation == .upMirrored { + transparentFrameLayer.transform = CATransform3DMakeScale(-1.0, 1.0, 1.0) + opaqueFrameLayer.transform = CATransform3DMakeScale(-1.0, 1.0, 1.0) + } + } + } + + let containerLeftEdge: CGFloat = 0.0 + let containerRightEdge: CGFloat = availableSize.width + + transition.setFrame(view: self.videoTransparentFramesContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: scrubberSize.width, height: scrubberSize.height))) + transition.setFrame(view: self.videoOpaqueFramesContainer, frame: CGRect(origin: CGPoint(x: containerLeftEdge, y: 0.0), size: CGSize(width: containerRightEdge - containerLeftEdge, height: scrubberSize.height))) + transition.setBounds(view: self.videoOpaqueFramesContainer, bounds: CGRect(origin: CGPoint(x: containerLeftEdge, y: 0.0), size: CGSize(width: containerRightEdge - containerLeftEdge, height: scrubberSize.height))) + + var frameAspectRatio = 0.66 + if let image = frames.first, image.size.height > 0.0 { + frameAspectRatio = max(0.66, image.size.width / image.size.height) + } + let frameSize = CGSize(width: trackHeight * frameAspectRatio, height: trackHeight) + var frameOffset: CGFloat = 0.0 + for i in 0 ..< frames.count { + if i < self.videoTransparentFrameLayers.count { + let transparentFrameLayer = self.videoTransparentFrameLayers[i] + let opaqueFrameLayer = self.videoOpaqueFrameLayers[i] + let frame = CGRect(origin: CGPoint(x: frameOffset, y: floorToScreenPixels((scrubberSize.height - frameSize.height) / 2.0)), size: frameSize) + + transparentFrameLayer.bounds = CGRect(origin: .zero, size: frame.size) + opaqueFrameLayer.bounds = CGRect(origin: .zero, size: frame.size) + + transition.setPosition(layer: transparentFrameLayer, position: frame.center) + transition.setPosition(layer: opaqueFrameLayer, position: frame.center) + } + frameOffset += frameSize.width + } + case let .audio(artist, title, samples, peak): + var components: [String] = [] + var trackTitle = "" + if let artist { + components.append(artist) + } + if let title { + components.append(title) + } + if components.isEmpty { + let strings = context.sharedContext.currentPresentationData.with { $0 }.strings + components.append(strings.MediaEditor_Audio) + } + trackTitle = components.joined(separator: " • ") + + let audioTitle = NSAttributedString(string: trackTitle, font: Font.semibold(13.0), textColor: .white) + let audioTitleSize: CGSize + if !trackTitle.isEmpty { + audioTitleSize = self.audioTitle.update( + transition: transition, + component: AnyComponent( + MultilineTextComponent( + text: .plain(audioTitle) + ) + ), + environment: {}, + containerSize: availableSize + ) + } else { + if let audioTitleView = self.audioTitle.view { + audioTitleSize = audioTitleView.bounds.size + } else { + audioTitleSize = .zero + } + } + + let spacing: CGFloat = 4.0 + let iconSize = CGSize(width: 14.0, height: 14.0) + let contentTotalWidth = iconSize.width + audioTitleSize.width + spacing + + let audioContentTransition = transition + transition.setAlpha(view: self.audioIconView, alpha: isSelected ? 0.0 : 1.0) + + let audioIconFrame = CGRect(origin: CGPoint(x: max(8.0, floorToScreenPixels((deselectedClipWidth - contentTotalWidth) / 2.0)), y: floorToScreenPixels((scrubberSize.height - iconSize.height) / 2.0)), size: iconSize) + audioContentTransition.setBounds(view: self.audioIconView, bounds: CGRect(origin: .zero, size: audioIconFrame.size)) + audioContentTransition.setPosition(view: self.audioIconView, position: audioIconFrame.center) + + let trackTitleIsVisible = !isSelected && !track.isMain && !trackTitle.isEmpty + if let view = self.audioTitle.view { + if view.superview == nil { + view.alpha = 0.0 + view.isUserInteractionEnabled = false + self.containerView.addSubview(self.backgroundView) + self.containerView.addSubview(self.audioContentContainerView) + self.audioContentContainerView.addSubview(self.audioIconView) + self.audioContentContainerView.addSubview(view) + } + transition.setAlpha(view: view, alpha: trackTitleIsVisible ? 1.0 : 0.0) + + let audioTitleFrame = CGRect(origin: CGPoint(x: audioIconFrame.maxX + spacing, y: floorToScreenPixels((scrubberSize.height - audioTitleSize.height) / 2.0)), size: audioTitleSize) + view.bounds = CGRect(origin: .zero, size: audioTitleFrame.size) + audioContentTransition.setPosition(view: view, position: audioTitleFrame.center) + } + transition.setAlpha(view: self.audioIconView, alpha: trackTitleIsVisible ? 1.0 : 0.0) + + var previousSamples: Data? + if let previousParams, case let .audio(_ , _, previousSamplesValue, _) = previousParams.track.content { + previousSamples = previousSamplesValue + } + + let samples = samples ?? Data() + if let view = self.audioWaveform.view, previousSamples == nil && !samples.isEmpty, let vibrancySnapshotView = view.snapshotContentTree(), let snapshotView = self.waveformCloneLayer.snapshotContentTreeAsView() { + vibrancySnapshotView.frame = view.frame + snapshotView.alpha = 0.3 + snapshotView.frame = view.frame + self.vibrancyContainer.addSubview(vibrancySnapshotView) + self.containerView.addSubview(snapshotView) + + vibrancySnapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + vibrancySnapshotView.removeFromSuperview() + }) + + snapshotView.layer.animateAlpha(from: 0.3, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + vibrancySnapshotView.removeFromSuperview() + }) + + view.layer.animateScaleY(from: 0.01, to: 1.0, duration: 0.2) + view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + + self.waveformCloneLayer.animateScaleY(from: 0.01, to: 1.0, duration: 0.2) + self.waveformCloneLayer.animateAlpha(from: 0.0, to: 0.3, duration: 0.2) + } + let audioWaveformSize = self.audioWaveform.update( + transition: transition, + component: AnyComponent( + AudioWaveformComponent( + backgroundColor: .clear, + foregroundColor: UIColor(rgb: 0xffffff, alpha: 0.3), + shimmerColor: nil, + style: .middle, + samples: samples, + peak: peak, + status: .complete(), + seek: nil, + updateIsSeeking: nil + ) + ), + environment: {}, + containerSize: CGSize(width: containerFrame.width, height: trackHeight) + ) + if let view = self.audioWaveform.view as? AudioWaveformComponent.View { + if view.superview == nil { + view.cloneLayer = self.waveformCloneLayer + self.vibrancyContainer.addSubview(view) + self.containerView.layer.addSublayer(self.waveformCloneLayer) + } + let audioWaveformFrame = CGRect(origin: CGPoint(x: 0.0, y: isSelected || track.isMain ? 0.0 : 6.0), size: audioWaveformSize) + transition.setFrame(view: view, frame: audioWaveformFrame) + transition.setFrame(layer: self.waveformCloneLayer, frame: audioWaveformFrame) + } + } + + return scrubberSize + } +} + + +private class TrimView: UIView { + fileprivate let leftHandleView = HandleView() + fileprivate let rightHandleView = HandleView() + private let borderView = UIImageView() + private let zoneView = HandleView() + + private let leftCapsuleView = UIView() + private let rightCapsuleView = UIView() + + fileprivate var isPanningTrimHandle = false + + var isHollow = false + + var trimUpdated: (Double, Double, Bool, Bool) -> Void = { _, _, _, _ in } + var updated: (Transition) -> Void = { _ in } + + override init(frame: CGRect) { + super.init(frame: .zero) + + let height = trackHeight + let handleImage = generateImage(CGSize(width: handleWidth, height: height), rotatedContext: { size, context in + context.clear(CGRect(origin: .zero, size: size)) + context.setFillColor(UIColor.white.cgColor) + + let path = UIBezierPath(roundedRect: CGRect(origin: .zero, size: CGSize(width: size.width * 2.0, height: size.height)), cornerRadius: 9.0) + context.addPath(path.cgPath) + context.fillPath() + + context.setBlendMode(.clear) + let innerPath = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: handleWidth - 3.0, y: borderHeight), size: CGSize(width: handleWidth, height: size.height - borderHeight * 2.0)), cornerRadius: 2.0) + context.addPath(innerPath.cgPath) + context.fillPath() + })?.withRenderingMode(.alwaysTemplate).resizableImage(withCapInsets: UIEdgeInsets(top: 10.0, left: 0.0, bottom: 10.0, right: 0.0)) + + self.zoneView.image = UIImage() + self.zoneView.isUserInteractionEnabled = true + self.zoneView.hitTestSlop = UIEdgeInsets(top: -8.0, left: 0.0, bottom: -8.0, right: 0.0) + + self.leftHandleView.image = handleImage + self.leftHandleView.isUserInteractionEnabled = true + self.leftHandleView.tintColor = .white + self.leftHandleView.contentMode = .scaleToFill + self.leftHandleView.hitTestSlop = UIEdgeInsets(top: -8.0, left: -9.0, bottom: -8.0, right: -9.0) + + self.rightHandleView.image = handleImage + self.rightHandleView.transform = CGAffineTransform(scaleX: -1.0, y: 1.0) + self.rightHandleView.isUserInteractionEnabled = true + self.rightHandleView.tintColor = .white + self.rightHandleView.contentMode = .scaleToFill + self.rightHandleView.hitTestSlop = UIEdgeInsets(top: -8.0, left: -9.0, bottom: -8.0, right: -9.0) + + self.borderView.image = generateImage(CGSize(width: 1.0, height: height), rotatedContext: { size, context in + context.clear(CGRect(origin: .zero, size: size)) + context.setFillColor(UIColor.white.cgColor) + context.fill(CGRect(origin: .zero, size: CGSize(width: size.width, height: borderHeight))) + context.fill(CGRect(origin: CGPoint(x: 0.0, y: size.height - borderHeight), size: CGSize(width: size.width, height: height))) + })?.withRenderingMode(.alwaysTemplate).resizableImage(withCapInsets: UIEdgeInsets(top: 10.0, left: 0.0, bottom: 10.0, right: 0.0)) + self.borderView.tintColor = .white + self.borderView.isUserInteractionEnabled = false + + self.leftCapsuleView.clipsToBounds = true + self.leftCapsuleView.layer.cornerRadius = 1.0 + self.leftCapsuleView.backgroundColor = UIColor(rgb: 0x343436) + + self.rightCapsuleView.clipsToBounds = true + self.rightCapsuleView.layer.cornerRadius = 1.0 + self.rightCapsuleView.backgroundColor = UIColor(rgb: 0x343436) + + self.addSubview(self.zoneView) + self.addSubview(self.leftHandleView) + self.leftHandleView.addSubview(self.leftCapsuleView) + + self.addSubview(self.rightHandleView) + self.rightHandleView.addSubview(self.rightCapsuleView) + self.addSubview(self.borderView) + + self.zoneView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handleZoneHandlePan(_:)))) + self.leftHandleView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handleLeftHandlePan(_:)))) + self.rightHandleView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handleRightHandlePan(_:)))) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func handleZoneHandlePan(_ gestureRecognizer: UIPanGestureRecognizer) { + guard let params = self.params else { + return + } + let translation = gestureRecognizer.translation(in: self) + + let start = handleWidth / 2.0 + let end = self.frame.width - handleWidth / 2.0 + let length = end - start + + let delta = translation.x / length + + let duration = params.endPosition - params.startPosition + let startValue = max(0.0, min(params.duration - duration, params.startPosition + delta * params.duration)) + let endValue = startValue + duration + + var transition: Transition = .immediate + switch gestureRecognizer.state { + case .began, .changed: + self.isPanningTrimHandle = true + self.trimUpdated(startValue, endValue, false, false) + if case .began = gestureRecognizer.state { + transition = .easeInOut(duration: 0.25) + } + case .ended, .cancelled: + self.isPanningTrimHandle = false + self.trimUpdated(startValue, endValue, false, true) + transition = .easeInOut(duration: 0.25) + default: + break + } + + gestureRecognizer.setTranslation(.zero, in: self) + self.updated(transition) + } + + @objc private func handleLeftHandlePan(_ gestureRecognizer: UIPanGestureRecognizer) { + guard let params = self.params else { + return + } + let location = gestureRecognizer.location(in: self) + let start = handleWidth / 2.0 + let end = params.scrubberSize.width - handleWidth / 2.0 + let length = end - start + let fraction = (location.x - start) / length + + var startValue = max(0.0, params.duration * fraction) + if startValue > params.endPosition - params.minDuration { + startValue = max(0.0, params.endPosition - params.minDuration) + } + var endValue = params.endPosition + if endValue - startValue > params.maxDuration { + let delta = (endValue - startValue) - params.maxDuration + endValue -= delta + } + + var transition: Transition = .immediate + switch gestureRecognizer.state { + case .began, .changed: + self.isPanningTrimHandle = true + self.trimUpdated(startValue, endValue, false, false) + if case .began = gestureRecognizer.state { + transition = .easeInOut(duration: 0.25) + } + case .ended, .cancelled: + self.isPanningTrimHandle = false + self.trimUpdated(startValue, endValue, false, true) + transition = .easeInOut(duration: 0.25) + default: + break + } + self.updated(transition) + } + + @objc private func handleRightHandlePan(_ gestureRecognizer: UIPanGestureRecognizer) { + guard let params = self.params else { + return + } + let location = gestureRecognizer.location(in: self) + let start = handleWidth / 2.0 + let end = params.scrubberSize.width - handleWidth / 2.0 + let length = end - start + let fraction = (location.x - start) / length + + var endValue = min(params.duration, params.duration * fraction) + if endValue < params.startPosition + params.minDuration { + endValue = min(params.duration, params.startPosition + params.minDuration) + } + var startValue = params.startPosition + if endValue - startValue > params.maxDuration { + let delta = (endValue - startValue) - params.maxDuration + startValue += delta + } + + var transition: Transition = .immediate + switch gestureRecognizer.state { + case .began, .changed: + self.isPanningTrimHandle = true + self.trimUpdated(startValue, endValue, true, false) + if case .began = gestureRecognizer.state { + transition = .easeInOut(duration: 0.25) + } + case .ended, .cancelled: + self.isPanningTrimHandle = false + self.trimUpdated(startValue, endValue, true, true) + transition = .easeInOut(duration: 0.25) + default: + break + } + self.updated(transition) + } + + var params: ( + scrubberSize: CGSize, + duration: Double, + startPosition: Double, + endPosition: Double, + position: Double, + minDuration: Double, + maxDuration: Double + )? + + func update( + visualInsets: UIEdgeInsets, + scrubberSize: CGSize, + duration: Double, + startPosition: Double, + endPosition: Double, + position: Double, + minDuration: Double, + maxDuration: Double, + transition: Transition + ) -> (leftHandleFrame: CGRect, rightHandleFrame: CGRect) + { + self.params = (scrubberSize, duration, startPosition, endPosition, position, minDuration, maxDuration) + + let trimColor = self.isPanningTrimHandle ? UIColor(rgb: 0xf8d74a) : .white + transition.setTintColor(view: self.leftHandleView, color: trimColor) + transition.setTintColor(view: self.rightHandleView, color: trimColor) + transition.setTintColor(view: self.borderView, color: trimColor) + + let totalWidth = scrubberSize.width + let totalRange = totalWidth - handleWidth + let leftHandlePositionFraction = duration > 0.0 ? startPosition / duration : 0.0 + let leftHandlePosition = floorToScreenPixels(handleWidth / 2.0 + totalRange * leftHandlePositionFraction) + + var leftHandleFrame = CGRect(origin: CGPoint(x: leftHandlePosition - handleWidth / 2.0, y: 0.0), size: CGSize(width: handleWidth, height: scrubberSize.height)) + leftHandleFrame.origin.x = max(leftHandleFrame.origin.x, visualInsets.left) + transition.setFrame(view: self.leftHandleView, frame: leftHandleFrame) + + let rightHandlePositionFraction = duration > 0.0 ? endPosition / duration : 1.0 + let rightHandlePosition = floorToScreenPixels(handleWidth / 2.0 + totalRange * rightHandlePositionFraction) + + var rightHandleFrame = CGRect(origin: CGPoint(x: max(leftHandleFrame.maxX, rightHandlePosition - handleWidth / 2.0), y: 0.0), size: CGSize(width: handleWidth, height: scrubberSize.height)) + rightHandleFrame.origin.x = min(rightHandleFrame.origin.x, totalWidth - visualInsets.right - handleWidth) + transition.setFrame(view: self.rightHandleView, frame: rightHandleFrame) + + let capsuleSize = CGSize(width: 2.0, height: 11.0) + transition.setFrame(view: self.leftCapsuleView, frame: CGRect(origin: CGPoint(x: 5.0 - UIScreenPixel, y: floorToScreenPixels((leftHandleFrame.height - capsuleSize.height) / 2.0)), size: capsuleSize)) + transition.setFrame(view: self.rightCapsuleView, frame: CGRect(origin: CGPoint(x: 5.0 - UIScreenPixel, y: floorToScreenPixels((leftHandleFrame.height - capsuleSize.height) / 2.0)), size: capsuleSize)) + + let zoneFrame = CGRect(x: leftHandleFrame.maxX, y: 0.0, width: rightHandleFrame.minX - leftHandleFrame.maxX, height: scrubberSize.height) + transition.setFrame(view: self.zoneView, frame: zoneFrame) + + let borderFrame = CGRect(origin: CGPoint(x: leftHandleFrame.maxX, y: 0.0), size: CGSize(width: rightHandleFrame.minX - leftHandleFrame.maxX, height: scrubberSize.height)) + transition.setFrame(view: self.borderView, frame: borderFrame) + + return (leftHandleFrame, rightHandleFrame) + } + + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + let leftHandleFrame = self.leftHandleView.frame.insetBy(dx: -8.0, dy: -9.0) + let rightHandleFrame = self.rightHandleView.frame.insetBy(dx: -8.0, dy: -9.0) + let areaFrame = CGRect(x: leftHandleFrame.minX, y: leftHandleFrame.minY, width: rightHandleFrame.maxX - leftHandleFrame.minX, height: rightHandleFrame.maxY - rightHandleFrame.minY) + + if self.isHollow { + return leftHandleFrame.contains(point) || rightHandleFrame.contains(point) + } else { + return areaFrame.contains(point) + } + } +} + + +private class VideoFrameLayer: SimpleShapeLayer { + private let stripeLayer = SimpleShapeLayer() + + override func layoutSublayers() { + super.layoutSublayers() + + if self.stripeLayer.superlayer == nil { + self.stripeLayer.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.3).cgColor + self.addSublayer(self.stripeLayer) + } + self.stripeLayer.frame = CGRect(x: self.bounds.width - UIScreenPixel, y: 0.0, width: UIScreenPixel, height: self.bounds.height) + } +} + +private final class HandleView: UIImageView { + var hitTestSlop = UIEdgeInsets() + + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + return self.bounds.inset(by: self.hitTestSlop).contains(point) + } +} + + +private let audioContentMaskImage = generateImage(CGSize(width: 100.0, height: 50.0), rotatedContext: { size, context in + context.clear(CGRect(origin: .zero, size: size)) + + var locations: [CGFloat] = [0.0, 0.75, 0.95, 1.0] + let colors: [CGColor] = [UIColor.white.cgColor, UIColor.white.cgColor, UIColor.white.withAlphaComponent(0.0).cgColor, UIColor.white.withAlphaComponent(0.0).cgColor] + + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)! + + context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: 0.0), options: CGGradientDrawingOptions()) +})?.stretchableImage(withLeftCapWidth: 40, topCapHeight: 0) + + +private extension MediaScrubberComponent.Track { + var isAudio: Bool { + if case .audio = self.content { + return true + } else { + return false + } + } +} diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaToolsScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaToolsScreen.swift index c1345d6bec1..e5013f5429e 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaToolsScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaToolsScreen.swift @@ -946,8 +946,6 @@ private final class MediaToolsScreenComponent: Component { } } -private let storyDimensions = CGSize(width: 1080.0, height: 1920.0) - public final class MediaToolsScreen: ViewController { fileprivate final class Node: ViewControllerTracingNode, UIGestureRecognizerDelegate { private weak var controller: MediaToolsScreen? diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StoryPreviewComponent.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StoryPreviewComponent.swift index 266eeee692a..70d1f620d25 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StoryPreviewComponent.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StoryPreviewComponent.swift @@ -262,7 +262,7 @@ final class StoryPreviewComponent: Component { sendMessageAction: { }, sendMessageOptionsAction: nil, sendStickerAction: { _ in }, - setMediaRecordingActive: { _, _, _ in }, + setMediaRecordingActive: { _, _, _, _ in }, lockMediaRecording: nil, stopAndPreviewMediaRecording: nil, discardMediaRecordingPreview: nil, @@ -281,6 +281,7 @@ final class StoryPreviewComponent: Component { audioRecorder: nil, videoRecordingStatus: nil, isRecordingLocked: false, + hasRecordedVideo: false, recordedAudioPreview: nil, hasRecordedVideoPreview: false, wasRecordingDismissed: false, @@ -293,6 +294,7 @@ final class StoryPreviewComponent: Component { customInputView: nil, forceIsEditing: false, disabledPlaceholder: nil, + header: nil, isChannel: false, storyItem: nil, chatLocation: nil diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/VideoScrubberComponent.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/VideoScrubberComponent.swift deleted file mode 100644 index 7bdc8a6b11b..00000000000 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/VideoScrubberComponent.swift +++ /dev/null @@ -1,1230 +0,0 @@ -import Foundation -import UIKit -import Display -import AsyncDisplayKit -import ComponentFlow -import SwiftSignalKit -import ViewControllerComponent -import ComponentDisplayAdapters -import TelegramPresentationData -import AccountContext -import AudioWaveformComponent -import MultilineTextComponent - -private let handleWidth: CGFloat = 14.0 -private let scrubberHeight: CGFloat = 39.0 -private let collapsedScrubberHeight: CGFloat = 26.0 -private let borderHeight: CGFloat = 1.0 + UIScreenPixel -private let frameWidth: CGFloat = 24.0 - -private class VideoFrameLayer: SimpleShapeLayer { - private let stripeLayer = SimpleShapeLayer() - - override func layoutSublayers() { - super.layoutSublayers() - - if self.stripeLayer.superlayer == nil { - self.stripeLayer.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.3).cgColor - self.addSublayer(self.stripeLayer) - } - self.stripeLayer.frame = CGRect(x: self.bounds.width - UIScreenPixel, y: 0.0, width: UIScreenPixel, height: self.bounds.height) - } -} - -private final class HandleView: UIImageView { - var hitTestSlop = UIEdgeInsets() - - override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { - return self.bounds.inset(by: self.hitTestSlop).contains(point) - } -} - -final class VideoScrubberComponent: Component { - typealias EnvironmentType = Empty - - struct AudioData: Equatable { - let artist: String? - let title: String? - let samples: Data? - let peak: Int32 - let duration: Double - let start: Double? - let end: Double? - let offset: Double? - } - - let context: AccountContext - let generationTimestamp: Double - let audioOnly: Bool - let duration: Double - let startPosition: Double - let endPosition: Double - let position: Double - let minDuration: Double - let maxDuration: Double - let isPlaying: Bool - let frames: [UIImage] - let framesUpdateTimestamp: Double - let audioData: AudioData? - let videoTrimUpdated: (Double, Double, Bool, Bool) -> Void - let positionUpdated: (Double, Bool) -> Void - let audioTrimUpdated: (Double, Double, Bool, Bool) -> Void - let audioOffsetUpdated: (Double, Bool) -> Void - let audioLongPressed: ((UIView) -> Void)? - - init( - context: AccountContext, - generationTimestamp: Double, - audioOnly: Bool, - duration: Double, - startPosition: Double, - endPosition: Double, - position: Double, - minDuration: Double, - maxDuration: Double, - isPlaying: Bool, - frames: [UIImage], - framesUpdateTimestamp: Double, - audioData: AudioData?, - videoTrimUpdated: @escaping (Double, Double, Bool, Bool) -> Void, - positionUpdated: @escaping (Double, Bool) -> Void, - audioTrimUpdated: @escaping (Double, Double, Bool, Bool) -> Void, - audioOffsetUpdated: @escaping (Double, Bool) -> Void, - audioLongPressed: ((UIView) -> Void)? - ) { - self.context = context - self.generationTimestamp = generationTimestamp - self.audioOnly = audioOnly - self.duration = duration - self.startPosition = startPosition - self.endPosition = endPosition - self.position = position - self.minDuration = minDuration - self.maxDuration = maxDuration - self.isPlaying = isPlaying - self.frames = frames - self.framesUpdateTimestamp = framesUpdateTimestamp - self.audioData = audioData - self.videoTrimUpdated = videoTrimUpdated - self.positionUpdated = positionUpdated - self.audioTrimUpdated = audioTrimUpdated - self.audioOffsetUpdated = audioOffsetUpdated - self.audioLongPressed = audioLongPressed - } - - static func ==(lhs: VideoScrubberComponent, rhs: VideoScrubberComponent) -> Bool { - if lhs.context !== rhs.context { - return false - } - if lhs.generationTimestamp != rhs.generationTimestamp { - return false - } - if lhs.audioOnly != rhs.audioOnly { - return false - } - if lhs.duration != rhs.duration { - return false - } - if lhs.startPosition != rhs.startPosition { - return false - } - if lhs.endPosition != rhs.endPosition { - return false - } - if lhs.position != rhs.position { - return false - } - if lhs.minDuration != rhs.minDuration { - return false - } - if lhs.maxDuration != rhs.maxDuration { - return false - } - if lhs.isPlaying != rhs.isPlaying { - return false - } - if lhs.framesUpdateTimestamp != rhs.framesUpdateTimestamp { - return false - } - if lhs.audioData != rhs.audioData { - return false - } - return true - } - - final class View: UIView, UIGestureRecognizerDelegate, UIScrollViewDelegate { - private let audioClippingView: UIView - private let audioScrollView: UIScrollView - private let audioContainerView: UIView - private let audioBackgroundView: BlurredBackgroundView - private let audioVibrancyView: UIVisualEffectView - private let audioVibrancyContainer: UIView - - private let audioContentContainerView: UIView - private let audioContentMaskView: UIImageView - private let audioIconView: UIImageView - private let audioTitle = ComponentView() - - private let audioWaveform = ComponentView() - private let waveformCloneLayer = AudioWaveformComponent.View.CloneLayer() - - private let trimView = TrimView(frame: .zero) - private let ghostTrimView = TrimView(frame: .zero) - - private let cursorView = HandleView() - - private let transparentFramesContainer = UIView() - private let opaqueFramesContainer = UIView() - - private var transparentFrameLayers: [VideoFrameLayer] = [] - private var opaqueFrameLayers: [VideoFrameLayer] = [] - - private var component: VideoScrubberComponent? - private weak var state: EmptyComponentState? - private var scrubberSize: CGSize? - - private var isAudioSelected = false - private var isPanningPositionHandle = false - - private var displayLink: SharedDisplayLinkDriver.Link? - private var positionAnimation: (start: Double, from: Double, to: Double, ended: Bool)? - - override init(frame: CGRect) { - self.audioScrollView = UIScrollView() - if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { - self.audioScrollView.contentInsetAdjustmentBehavior = .never - } - if #available(iOS 13.0, *) { - self.audioScrollView.automaticallyAdjustsScrollIndicatorInsets = false - } - self.audioScrollView.bounces = false - self.audioScrollView.decelerationRate = .fast - self.audioScrollView.clipsToBounds = false - self.audioScrollView.showsHorizontalScrollIndicator = false - self.audioScrollView.showsVerticalScrollIndicator = false - - self.audioClippingView = UIView() - self.audioClippingView.clipsToBounds = true - - self.audioContainerView = UIView() - self.audioContainerView.clipsToBounds = true - self.audioContainerView.layer.cornerRadius = 9.0 - self.audioContainerView.isUserInteractionEnabled = false - - self.audioBackgroundView = BlurredBackgroundView(color: UIColor(white: 0.0, alpha: 0.5), enableBlur: true) - - let style: UIBlurEffect.Style = .dark - let blurEffect = UIBlurEffect(style: style) - let vibrancyEffect = UIVibrancyEffect(blurEffect: blurEffect) - let vibrancyEffectView = UIVisualEffectView(effect: vibrancyEffect) - self.audioVibrancyView = vibrancyEffectView - - self.audioVibrancyContainer = UIView() - self.audioVibrancyView.contentView.addSubview(self.audioVibrancyContainer) - - self.audioContentContainerView = UIView() - self.audioContentContainerView.clipsToBounds = true - - self.audioContentMaskView = UIImageView() - self.audioContentContainerView.mask = self.audioContentMaskView - - self.audioIconView = UIImageView(image: UIImage(bundleImageName: "Media Editor/SmallAudio")) - - self.waveformCloneLayer.opacity = 0.3 - - super.init(frame: frame) - - self.clipsToBounds = false - - self.audioScrollView.delegate = self - - self.disablesInteractiveModalDismiss = true - self.disablesInteractiveKeyboardGestureRecognizer = true - - let positionImage = generateImage(CGSize(width: handleWidth, height: 50.0), rotatedContext: { size, context in - context.clear(CGRect(origin: .zero, size: size)) - context.setFillColor(UIColor.white.cgColor) - context.setShadow(offset: .zero, blur: 2.0, color: UIColor(rgb: 0x000000, alpha: 0.55).cgColor) - - let path = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: 6.0, y: 4.0), size: CGSize(width: 2.0, height: 42.0)), cornerRadius: 1.0) - context.addPath(path.cgPath) - context.fillPath() - })?.stretchableImage(withLeftCapWidth: Int(handleWidth / 2.0), topCapHeight: 25) - - self.cursorView.image = positionImage - self.cursorView.isUserInteractionEnabled = true - self.cursorView.hitTestSlop = UIEdgeInsets(top: -8.0, left: -9.0, bottom: -8.0, right: -9.0) - - self.transparentFramesContainer.alpha = 0.5 - self.transparentFramesContainer.clipsToBounds = true - self.transparentFramesContainer.layer.cornerRadius = 9.0 - self.transparentFramesContainer.isUserInteractionEnabled = false - - self.opaqueFramesContainer.clipsToBounds = true - self.opaqueFramesContainer.layer.cornerRadius = 9.0 - self.opaqueFramesContainer.isUserInteractionEnabled = false - - self.addSubview(self.audioClippingView) - self.audioClippingView.addSubview(self.audioScrollView) - self.audioScrollView.addSubview(self.audioContainerView) - self.audioContainerView.addSubview(self.audioBackgroundView) - self.audioBackgroundView.addSubview(self.audioVibrancyView) - - self.addSubview(self.audioIconView) - - self.addSubview(self.transparentFramesContainer) - self.addSubview(self.opaqueFramesContainer) - self.addSubview(self.ghostTrimView) - self.addSubview(self.trimView) - - self.addSubview(self.cursorView) - - self.cursorView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handlePositionHandlePan(_:)))) - - self.displayLink = SharedDisplayLinkDriver.shared.add { [weak self] in - self?.updateCursorPosition() - } - self.displayLink?.isPaused = true - - self.trimView.updated = { [weak self] transition in - self?.state?.updated(transition: transition) - } - - self.trimView.trimUpdated = { [weak self] startValue, endValue, updatedEnd, done in - if let self, let component = self.component { - if self.isAudioSelected || component.audioOnly { - component.audioTrimUpdated(startValue, endValue, updatedEnd, done) - } else { - component.videoTrimUpdated(startValue, endValue, updatedEnd, done) - } - } - } - - self.ghostTrimView.trimUpdated = { [weak self] startValue, endValue, updatedEnd, done in - if let self, let component = self.component { - component.videoTrimUpdated(startValue, endValue, updatedEnd, done) - } - } - - let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(self.longPressed(_:))) - longPressGesture.delegate = self - self.addGestureRecognizer(longPressGesture) - - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:))) - self.addGestureRecognizer(tapGesture) - - let maskImage = generateImage(CGSize(width: 100.0, height: 50.0), rotatedContext: { size, context in - context.clear(CGRect(origin: .zero, size: size)) - - var locations: [CGFloat] = [0.0, 0.75, 0.95, 1.0] - let colors: [CGColor] = [UIColor.white.cgColor, UIColor.white.cgColor, UIColor.white.withAlphaComponent(0.0).cgColor, UIColor.white.withAlphaComponent(0.0).cgColor] - - let colorSpace = CGColorSpaceCreateDeviceRGB() - let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)! - - context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: 0.0), options: CGGradientDrawingOptions()) - })?.stretchableImage(withLeftCapWidth: 40, topCapHeight: 0) - self.audioContentMaskView.image = maskImage - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - deinit { - self.displayLink?.invalidate() - } - - override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { - guard let component = self.component, component.audioData != nil else { - return false - } - let location = gestureRecognizer.location(in: self.audioContainerView) - return self.audioContainerView.bounds.contains(location) - } - - var ignoreScrollUpdates = false - private func updateAudioOffset(done: Bool) { - guard self.audioScrollView.contentSize.width > 0.0, let component = self.component, let duration = self.component?.audioData?.duration else { - return - } - let totalWidth = self.audioScrollView.contentSize.width - let offset = self.audioScrollView.contentOffset.x * duration / totalWidth - component.audioOffsetUpdated(offset, done) - } - - var isDragging = false - func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { - self.isDragging = true - self.state?.updated(transition: .easeInOut(duration: 0.25)) - } - - func scrollViewDidScroll(_ scrollView: UIScrollView) { - guard !self.ignoreScrollUpdates else { - return - } - self.updateAudioOffset(done: false) - } - - func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { - if !decelerate { - self.updateAudioOffset(done: true) - self.isDragging = false - self.state?.updated(transition: .easeInOut(duration: 0.25)) - } - } - - func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { - self.updateAudioOffset(done: true) - self.isDragging = false - self.state?.updated(transition: .easeInOut(duration: 0.25)) - } - - @objc private func longPressed(_ gestureRecognizer: UILongPressGestureRecognizer) { - guard let component = self.component, component.audioData != nil, case .began = gestureRecognizer.state else { - return - } - component.audioLongPressed?(self.audioClippingView) - } - - @objc private func handleTap(_ gestureRecognizer: UITapGestureRecognizer) { - guard let component = self.component, component.audioData != nil && !component.audioOnly else { - return - } - let location = gestureRecognizer.location(in: self) - if location.y < self.frame.height / 2.0 { - if self.isAudioSelected { - component.audioLongPressed?(self.audioClippingView) - } else { - self.isAudioSelected = true - } - } else { - self.isAudioSelected = false - } - self.state?.updated(transition: .easeInOut(duration: 0.25)) - } - - @objc private func handlePositionHandlePan(_ gestureRecognizer: UIPanGestureRecognizer) { - guard let component = self.component else { - return - } - - var trimDuration = component.duration - if component.audioOnly, let audioData = component.audioData { - trimDuration = min(30.0, audioData.duration) - } - - let location = gestureRecognizer.location(in: self) - let start = handleWidth - let end = self.frame.width - handleWidth - let length = end - start - let fraction = (location.x - start) / length - - var position = max(component.startPosition, min(component.endPosition, trimDuration * fraction)) - if component.audioOnly, let offset = component.audioData?.offset { - position += offset - } - let transition: Transition = .immediate - switch gestureRecognizer.state { - case .began, .changed: - self.isPanningPositionHandle = true - component.positionUpdated(position, false) - case .ended, .cancelled: - self.isPanningPositionHandle = false - component.positionUpdated(position, true) - default: - break - } - self.state?.updated(transition: transition) - } - - private func cursorFrame(size: CGSize, height: CGFloat, position: Double, duration : Double) -> CGRect { - let cursorPadding: CGFloat = 8.0 - let cursorPositionFraction = duration > 0.0 ? position / duration : 0.0 - let cursorPosition = floorToScreenPixels(handleWidth - 1.0 + (size.width - handleWidth * 2.0 + 2.0) * cursorPositionFraction) - var cursorFrame = CGRect(origin: CGPoint(x: cursorPosition - handleWidth / 2.0, y: -5.0 - UIScreenPixel), size: CGSize(width: handleWidth, height: height)) - - var leftEdge = self.ghostTrimView.leftHandleView.frame.maxX - var rightEdge = self.ghostTrimView.rightHandleView.frame.minX - if let component = self.component, component.audioOnly { - leftEdge = self.trimView.leftHandleView.frame.maxX - rightEdge = self.trimView.rightHandleView.frame.minX - } - - cursorFrame.origin.x = max(leftEdge - cursorPadding, cursorFrame.origin.x) - cursorFrame.origin.x = min(rightEdge - handleWidth + cursorPadding, cursorFrame.origin.x) - return cursorFrame - } - - private func updateCursorPosition() { - guard let component = self.component, let scrubberSize = self.scrubberSize else { - return - } - let timestamp = CACurrentMediaTime() - - var trimDuration = component.duration - if component.audioOnly, let audioData = component.audioData { - trimDuration = min(30.0, audioData.duration) - } - - let updatedPosition: Double - if let (start, from, to, _) = self.positionAnimation { - var from = from - if component.audioOnly, let offset = component.audioData?.offset { - from -= offset - } - let duration = to - from - let fraction = duration > 0.0 ? (timestamp - start) / duration : 0.0 - updatedPosition = max(component.startPosition, min(component.endPosition, from + (to - from) * fraction)) - if fraction >= 1.0 { - self.positionAnimation = (start, from, to, true) - } - } else { - var position = component.position - if component.audioOnly, let offset = component.audioData?.offset { - position -= offset - } - let advance = component.isPlaying ? timestamp - component.generationTimestamp : 0.0 - updatedPosition = max(component.startPosition, min(component.endPosition, position + advance)) - } - let cursorHeight: CGFloat = component.audioData != nil && !component.audioOnly ? 80.0 : 50.0 - self.cursorView.frame = cursorFrame(size: scrubberSize, height: cursorHeight, position: updatedPosition, duration: trimDuration) - } - - func update(component: VideoScrubberComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { - let previousComponent = self.component - let previousFramesUpdateTimestamp = self.component?.framesUpdateTimestamp - self.component = component - self.state = state - - var trimDuration = component.duration - - var audioChanged = false - var animateAudioAppearance = false - if let previousComponent { - if let previousAudioData = previousComponent.audioData, previousAudioData.title != component.audioData?.title { - audioChanged = true - } - if previousComponent.audioData == nil, component.audioData != nil { - self.positionAnimation = nil - animateAudioAppearance = true - } else if previousComponent.audioData != nil, component.audioData == nil { - self.positionAnimation = nil - self.isAudioSelected = false - animateAudioAppearance = true - } - } - - let scrubberSpacing: CGFloat = 4.0 - - var audioScrubberHeight: CGFloat = collapsedScrubberHeight - var videoScrubberHeight: CGFloat = scrubberHeight - - let scrubberSize = CGSize(width: availableSize.width, height: scrubberHeight) - self.scrubberSize = scrubberSize - - var audioTransition = transition - var videoTransition = transition - if animateAudioAppearance { - audioTransition = .easeInOut(duration: 0.25) - videoTransition = .easeInOut(duration: 0.25) - } - - let totalWidth = scrubberSize.width - handleWidth - var audioTotalWidth = scrubberSize.width - - let minimalAudioWidth = handleWidth * 2.0 - - var originY: CGFloat = 0 - var totalHeight = scrubberSize.height - var audioAlpha: CGFloat = 0.0 - if let audioData = component.audioData { - if component.audioOnly { - trimDuration = min(30.0, audioData.duration) - - audioScrubberHeight = scrubberHeight - audioAlpha = 1.0 - } else { - totalHeight += collapsedScrubberHeight + scrubberSpacing - audioAlpha = 1.0 - - originY += self.isAudioSelected ? scrubberHeight : collapsedScrubberHeight - originY += scrubberSpacing - - if self.isAudioSelected { - audioScrubberHeight = scrubberHeight - videoScrubberHeight = collapsedScrubberHeight - } - } - if trimDuration > 0.0 { - let audioFraction = audioData.duration / trimDuration - if audioFraction < 1.0 - .ulpOfOne || audioFraction > 1.0 + .ulpOfOne { - audioTotalWidth = max(minimalAudioWidth, ceil(totalWidth * audioFraction)) - } - } - } else { - self.isAudioSelected = false - } - audioTransition.setAlpha(view: self.audioClippingView, alpha: audioAlpha) - - var audioClipOrigin: CGFloat = -9.0 - var audioClipWidth = availableSize.width + 18.0 - - var deselectedAudioClipWidth: CGFloat = 0.0 - var deselectedAudioClipOrigin: CGFloat = 0.0 - if let audioData = component.audioData, !component.audioOnly { - let duration: Double - if component.duration > 0.0 { - if let end = audioData.end, let start = audioData.start { - duration = end - start - } else { - duration = component.duration - } - - let fraction = duration / component.duration - deselectedAudioClipWidth = max(minimalAudioWidth, availableSize.width * fraction) - deselectedAudioClipOrigin = (audioData.start ?? 0.0) / component.duration * availableSize.width - - if self.audioScrollView.contentOffset.x < 0.0 { - deselectedAudioClipOrigin -= self.audioScrollView.contentOffset.x - if self.audioScrollView.contentSize.width > self.audioScrollView.frame.width { - deselectedAudioClipWidth += self.audioScrollView.contentOffset.x - } - } else if self.audioScrollView.contentSize.width > self.audioScrollView.frame.width, self.audioScrollView.contentOffset.x > self.audioScrollView.contentSize.width - self.audioScrollView.frame.width { - let delta = self.audioScrollView.contentOffset.x - (self.audioScrollView.contentSize.width - self.audioScrollView.frame.width) - deselectedAudioClipWidth -= delta - } - } - } - - if !self.isAudioSelected && !component.audioOnly { - if let _ = component.audioData { - audioClipOrigin = deselectedAudioClipOrigin - audioClipWidth = deselectedAudioClipWidth - } else { - audioClipWidth = availableSize.width - } - } - - let audioClippingFrame = CGRect(origin: CGPoint(x: audioClipOrigin, y: 0.0), size: CGSize(width: audioClipWidth, height: audioScrubberHeight)) - let audioClippingBounds = CGRect(origin: CGPoint(x: audioClipOrigin, y: 0.0), size: CGSize(width: audioClipWidth, height: audioScrubberHeight)) - audioTransition.setFrame(view: self.audioClippingView, frame: audioClippingFrame) - audioTransition.setBounds(view: self.audioClippingView, bounds: audioClippingBounds) - - self.audioScrollView.isUserInteractionEnabled = self.isAudioSelected || component.audioOnly - - self.ignoreScrollUpdates = true - - let audioScrollFrame = CGRect(origin: .zero, size: CGSize(width: availableSize.width, height: audioScrubberHeight)) - audioTransition.setFrame(view: self.audioScrollView, frame: audioScrollFrame) - - let contentSize = CGSize(width: audioTotalWidth, height: 39.0) - if self.audioScrollView.contentSize != contentSize || audioChanged { - self.audioScrollView.contentSize = contentSize - if !component.audioOnly { - let leftInset = scrubberSize.width - handleWidth * 2.5 - let rightInset: CGFloat - if self.audioScrollView.contentSize.width > self.audioScrollView.frame.width { - rightInset = scrubberSize.width - handleWidth * 2.5 - } else { - rightInset = self.audioScrollView.frame.width - self.audioScrollView.contentSize.width - } - self.audioScrollView.contentInset = UIEdgeInsets(top: 0.0, left: leftInset, bottom: 0.0, right: rightInset) - } - - if let offset = component.audioData?.offset, let duration = component.audioData?.duration, duration > 0.0 { - let contentOffset = offset * audioTotalWidth / duration - self.audioScrollView.contentOffset = CGPoint(x: contentOffset, y: 0.0) - } else { - self.audioScrollView.contentOffset = .zero - } - } - - self.ignoreScrollUpdates = false - - audioTransition.setCornerRadius(layer: self.audioClippingView.layer, cornerRadius: self.isAudioSelected ? 0.0 : 9.0) - - let audioContainerFrame = CGRect(origin: .zero, size: CGSize(width: audioTotalWidth, height: audioScrubberHeight)) - audioTransition.setFrame(view: self.audioContainerView, frame: audioContainerFrame) - - audioTransition.setFrame(view: self.audioBackgroundView, frame: CGRect(origin: .zero, size: audioContainerFrame.size)) - self.audioBackgroundView.update(size: audioContainerFrame.size, transition: audioTransition.containedViewLayoutTransition) - audioTransition.setFrame(view: self.audioVibrancyView, frame: CGRect(origin: .zero, size: audioContainerFrame.size)) - audioTransition.setFrame(view: self.audioVibrancyContainer, frame: CGRect(origin: .zero, size: audioContainerFrame.size)) - - let containerFrame = CGRect(origin: .zero, size: CGSize(width: audioClipWidth, height: audioContainerFrame.height)) - let contentContainerOrigin = deselectedAudioClipOrigin + self.audioScrollView.contentOffset.x - audioTransition.setFrame(view: self.audioContentContainerView, frame: containerFrame.offsetBy(dx: contentContainerOrigin, dy: 0.0)) - audioTransition.setFrame(view: self.audioContentMaskView, frame: CGRect(origin: .zero, size: containerFrame.size)) - - var components: [String] = [] - var trackTitle = "" - if let audioData = component.audioData { - if let artist = audioData.artist { - components.append(artist) - } - if let title = audioData.title { - components.append(title) - } - if components.isEmpty { - let strings = component.context.sharedContext.currentPresentationData.with { $0 }.strings - components.append(strings.MediaEditor_Audio) - } - trackTitle = components.joined(separator: " • ") - } - - let audioTitle = NSAttributedString(string: trackTitle, font: Font.semibold(13.0), textColor: .white) - let audioTitleSize: CGSize - if !trackTitle.isEmpty { - audioTitleSize = self.audioTitle.update( - transition: transition, - component: AnyComponent( - MultilineTextComponent( - text: .plain(audioTitle) - ) - ), - environment: {}, - containerSize: availableSize - ) - } else { - if let audioTitleView = self.audioTitle.view { - audioTitleSize = audioTitleView.bounds.size - } else { - audioTitleSize = .zero - } - } - - let spacing: CGFloat = 4.0 - let iconSize = CGSize(width: 14.0, height: 14.0) - let contentTotalWidth = iconSize.width + audioTitleSize.width + spacing - - var audioContentTransition = audioTransition - if animateAudioAppearance, component.audioData != nil { - audioContentTransition = .immediate - } - audioTransition.setAlpha(view: self.audioIconView, alpha: self.isAudioSelected ? 0.0 : 1.0) - - let audioIconFrame = CGRect(origin: CGPoint(x: max(8.0, floorToScreenPixels((deselectedAudioClipWidth - contentTotalWidth) / 2.0)), y: floorToScreenPixels((audioScrubberHeight - iconSize.height) / 2.0)), size: iconSize) - audioContentTransition.setBounds(view: self.audioIconView, bounds: CGRect(origin: .zero, size: audioIconFrame.size)) - audioContentTransition.setPosition(view: self.audioIconView, position: audioIconFrame.center) - - let trackTitleIsVisible = !self.isAudioSelected && !component.audioOnly && !trackTitle.isEmpty - if let view = self.audioTitle.view { - if view.superview == nil { - view.alpha = 0.0 - view.isUserInteractionEnabled = false - self.audioContainerView.addSubview(self.audioContentContainerView) - self.audioContentContainerView.addSubview(self.audioIconView) - self.audioContentContainerView.addSubview(view) - } - audioTransition.setAlpha(view: view, alpha: trackTitleIsVisible ? 1.0 : 0.0) - - let audioTitleFrame = CGRect(origin: CGPoint(x: audioIconFrame.maxX + spacing, y: floorToScreenPixels((audioScrubberHeight - audioTitleSize.height) / 2.0)), size: audioTitleSize) - view.bounds = CGRect(origin: .zero, size: audioTitleFrame.size) - audioContentTransition.setPosition(view: view, position: audioTitleFrame.center) - } - audioTransition.setAlpha(view: self.audioIconView, alpha: trackTitleIsVisible ? 1.0 : 0.0) - - if let audioData = component.audioData { - let samples = audioData.samples ?? Data() - - if let view = self.audioWaveform.view, previousComponent?.audioData?.samples == nil && audioData.samples != nil, let vibrancySnapshotView = view.snapshotContentTree(), let snapshotView = self.waveformCloneLayer.snapshotContentTreeAsView() { - vibrancySnapshotView.frame = view.frame - snapshotView.alpha = 0.3 - snapshotView.frame = view.frame - self.audioVibrancyContainer.addSubview(vibrancySnapshotView) - self.audioContainerView.addSubview(snapshotView) - - vibrancySnapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in - vibrancySnapshotView.removeFromSuperview() - }) - - snapshotView.layer.animateAlpha(from: 0.3, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in - vibrancySnapshotView.removeFromSuperview() - }) - - view.layer.animateScaleY(from: 0.01, to: 1.0, duration: 0.2) - view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - - self.waveformCloneLayer.animateScaleY(from: 0.01, to: 1.0, duration: 0.2) - self.waveformCloneLayer.animateAlpha(from: 0.0, to: 0.3, duration: 0.2) - } - let audioWaveformSize = self.audioWaveform.update( - transition: transition, - component: AnyComponent( - AudioWaveformComponent( - backgroundColor: .clear, - foregroundColor: UIColor(rgb: 0xffffff, alpha: 0.3), - shimmerColor: nil, - style: .middle, - samples: samples, - peak: audioData.peak, - status: .complete(), - seek: nil, - updateIsSeeking: nil - ) - ), - environment: {}, - containerSize: CGSize(width: audioContainerFrame.width, height: scrubberHeight) - ) - if let view = self.audioWaveform.view as? AudioWaveformComponent.View { - if view.superview == nil { - view.cloneLayer = self.waveformCloneLayer - self.audioVibrancyContainer.addSubview(view) - self.audioContainerView.layer.addSublayer(self.waveformCloneLayer) - } - audioTransition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: 0.0, y: self.isAudioSelected || component.audioOnly ? 0.0 : 6.0), size: audioWaveformSize)) - - audioTransition.setFrame(layer: self.waveformCloneLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: self.isAudioSelected || component.audioOnly ? 0.0 : 6.0), size: audioWaveformSize)) - } - } - - let bounds = CGRect(origin: .zero, size: scrubberSize) - - if component.framesUpdateTimestamp != previousFramesUpdateTimestamp { - for i in 0 ..< component.frames.count { - let transparentFrameLayer: VideoFrameLayer - let opaqueFrameLayer: VideoFrameLayer - if i >= self.transparentFrameLayers.count { - transparentFrameLayer = VideoFrameLayer() - transparentFrameLayer.masksToBounds = true - transparentFrameLayer.contentsGravity = .resizeAspectFill - self.transparentFramesContainer.layer.addSublayer(transparentFrameLayer) - self.transparentFrameLayers.append(transparentFrameLayer) - opaqueFrameLayer = VideoFrameLayer() - opaqueFrameLayer.masksToBounds = true - opaqueFrameLayer.contentsGravity = .resizeAspectFill - self.opaqueFramesContainer.layer.addSublayer(opaqueFrameLayer) - self.opaqueFrameLayers.append(opaqueFrameLayer) - } else { - transparentFrameLayer = self.transparentFrameLayers[i] - opaqueFrameLayer = self.opaqueFrameLayers[i] - } - transparentFrameLayer.contents = component.frames[i].cgImage - if let contents = opaqueFrameLayer.contents, (contents as! CGImage) !== component.frames[i].cgImage, opaqueFrameLayer.animation(forKey: "contents") == nil { - opaqueFrameLayer.contents = component.frames[i].cgImage - opaqueFrameLayer.animate(from: contents as AnyObject, to: component.frames[i].cgImage! as AnyObject, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 0.2) - } else { - opaqueFrameLayer.contents = component.frames[i].cgImage - } - } - } - - var startPosition = component.startPosition - var endPosition = component.endPosition - if self.isAudioSelected || component.audioOnly, let audioData = component.audioData { - if let start = audioData.start { - startPosition = start - } - if let end = audioData.end { - endPosition = end - } - } - - var trimViewOffset: CGFloat = 0.0 - var trimViewVisualInsets: UIEdgeInsets = .zero - if self.isAudioSelected { - if self.audioScrollView.contentOffset.x < 0.0 { - trimViewOffset = -self.audioScrollView.contentOffset.x - trimViewVisualInsets.right = trimViewOffset - } else if self.audioScrollView.contentSize.width > self.audioScrollView.frame.width, self.audioScrollView.contentOffset.x > self.audioScrollView.contentSize.width - self.audioScrollView.frame.width { - let delta = self.audioScrollView.contentOffset.x - (self.audioScrollView.contentSize.width - self.audioScrollView.frame.width) - trimViewOffset = -delta - trimViewVisualInsets.left = delta - } - } - - self.trimView.isHollow = self.isAudioSelected || component.audioOnly - let (leftHandleFrame, rightHandleFrame) = self.trimView.update( - totalWidth: totalWidth, - visualInsets: trimViewVisualInsets, - scrubberSize: scrubberSize, - duration: trimDuration, - startPosition: startPosition, - endPosition: endPosition, - position: component.position, - minDuration: component.minDuration, - maxDuration: component.maxDuration, - transition: transition - ) - - let (ghostLeftHandleFrame, ghostRightHandleFrame) = self.ghostTrimView.update( - totalWidth: totalWidth, - visualInsets: .zero, - scrubberSize: CGSize(width: scrubberSize.width, height: collapsedScrubberHeight), - duration: component.duration, - startPosition: component.startPosition, - endPosition: component.endPosition, - position: component.position, - minDuration: component.minDuration, - maxDuration: component.maxDuration, - transition: transition - ) - - var containerLeftEdge = leftHandleFrame.maxX - var containerRightEdge = rightHandleFrame.minX - if self.isAudioSelected && component.duration > 0.0 { - containerLeftEdge = ghostLeftHandleFrame.maxX - containerRightEdge = ghostRightHandleFrame.minX - } - - let isDraggingAudio = self.isDragging - let isCursorHidden = isDraggingAudio || self.trimView.isPanningTrimHandle || self.ghostTrimView.isPanningTrimHandle - var cursorTransition = transition - if isCursorHidden { - cursorTransition = .immediate - } - cursorTransition.setAlpha(view: self.cursorView, alpha: isCursorHidden ? 0.0 : 1.0, delay: self.cursorView.alpha.isZero && !isCursorHidden ? 0.25 : 0.0) - - if self.isPanningPositionHandle || !component.isPlaying { - self.positionAnimation = nil - self.displayLink?.isPaused = true - - let cursorHeight: CGFloat = component.audioData != nil && !component.audioOnly ? 80.0 : 50.0 - var cursorPosition = component.position - if component.audioOnly, let audioOffset = component.audioData?.offset { - cursorPosition -= audioOffset - } - videoTransition.setFrame(view: self.cursorView, frame: cursorFrame(size: scrubberSize, height: cursorHeight, position: cursorPosition, duration: trimDuration)) - } else { - if let (_, _, end, ended) = self.positionAnimation { - if ended, component.position >= component.startPosition && component.position < end - 1.0 { - self.positionAnimation = (CACurrentMediaTime(), component.position, component.endPosition, false) - } - } else { - self.positionAnimation = (CACurrentMediaTime(), component.position, component.endPosition, false) - } - self.displayLink?.isPaused = false - self.updateCursorPosition() - } - - let trimViewFrame = CGRect(origin: CGPoint(x: trimViewOffset, y: self.isAudioSelected ? 0.0 : originY), size: bounds.size) - videoTransition.setFrame(view: self.trimView, frame: trimViewFrame) - - videoTransition.setFrame(view: self.ghostTrimView, frame: bounds.offsetBy(dx: 0.0, dy: originY)) - videoTransition.setAlpha(view: self.ghostTrimView, alpha: self.isAudioSelected ? 0.75 : 0.0) - - let handleInset: CGFloat = 7.0 - videoTransition.setFrame(view: self.transparentFramesContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: originY), size: CGSize(width: scrubberSize.width, height: videoScrubberHeight))) - videoTransition.setFrame(view: self.opaqueFramesContainer, frame: CGRect(origin: CGPoint(x: containerLeftEdge - handleInset, y: originY), size: CGSize(width: containerRightEdge - containerLeftEdge + handleInset * 2.0, height: videoScrubberHeight))) - videoTransition.setBounds(view: self.opaqueFramesContainer, bounds: CGRect(origin: CGPoint(x: containerLeftEdge - handleInset, y: 0.0), size: CGSize(width: containerRightEdge - containerLeftEdge + handleInset * 2.0, height: videoScrubberHeight))) - - videoTransition.setCornerRadius(layer: self.opaqueFramesContainer.layer, cornerRadius: self.isAudioSelected ? 9.0 : 0.0) - - var frameAspectRatio = 0.66 - if let image = component.frames.first, image.size.height > 0.0 { - frameAspectRatio = max(0.66, image.size.width / image.size.height) - } - let frameSize = CGSize(width: 39.0 * frameAspectRatio, height: 39.0) - var frameOffset: CGFloat = 0.0 - for i in 0 ..< component.frames.count { - if i < self.transparentFrameLayers.count { - let transparentFrameLayer = self.transparentFrameLayers[i] - let opaqueFrameLayer = self.opaqueFrameLayers[i] - let frame = CGRect(origin: CGPoint(x: frameOffset, y: floorToScreenPixels((videoScrubberHeight - frameSize.height) / 2.0)), size: frameSize) - - transparentFrameLayer.bounds = CGRect(origin: .zero, size: frame.size) - opaqueFrameLayer.bounds = CGRect(origin: .zero, size: frame.size) - - videoTransition.setPosition(layer: transparentFrameLayer, position: frame.center) - videoTransition.setPosition(layer: opaqueFrameLayer, position: frame.center) - } - frameOffset += frameSize.width - } - - return CGSize(width: availableSize.width, height: totalHeight) - } - - override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { - let hitTestSlop = UIEdgeInsets(top: -8.0, left: -9.0, bottom: -8.0, right: -9.0) - return self.bounds.inset(by: hitTestSlop).contains(point) - } - - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - let result = super.hitTest(point, with: event) - return result - } - } - - public func makeView() -> View { - return View(frame: CGRect()) - } - - public func update(view: View, availableSize: CGSize, state: State, environment: Environment, transition: Transition) -> CGSize { - return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) - } -} - -private class TrimView: UIView { - fileprivate let leftHandleView = HandleView() - fileprivate let rightHandleView = HandleView() - private let borderView = UIImageView() - private let zoneView = HandleView() - - private let leftCapsuleView = UIView() - private let rightCapsuleView = UIView() - - fileprivate var isPanningTrimHandle = false - - var isHollow = false - - var trimUpdated: (Double, Double, Bool, Bool) -> Void = { _, _, _, _ in } - var updated: (Transition) -> Void = { _ in } - - override init(frame: CGRect) { - super.init(frame: .zero) - - let height = scrubberHeight - let handleImage = generateImage(CGSize(width: handleWidth, height: height), rotatedContext: { size, context in - context.clear(CGRect(origin: .zero, size: size)) - context.setFillColor(UIColor.white.cgColor) - - let path = UIBezierPath(roundedRect: CGRect(origin: .zero, size: CGSize(width: size.width * 2.0, height: size.height)), cornerRadius: 9.0) - context.addPath(path.cgPath) - context.fillPath() - - context.setBlendMode(.clear) - let innerPath = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: handleWidth - 3.0, y: borderHeight), size: CGSize(width: handleWidth, height: size.height - borderHeight * 2.0)), cornerRadius: 2.0) - context.addPath(innerPath.cgPath) - context.fillPath() - -// if !ghost { -// context.setBlendMode(.clear) -// let holeSize = CGSize(width: 2.0, height: 11.0) -// let holePath = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: 5.0 - UIScreenPixel, y: (size.height - holeSize.height) / 2.0), size: holeSize), cornerRadius: holeSize.width / 2.0) -// context.addPath(holePath.cgPath) -// context.fillPath() -// } - })?.withRenderingMode(.alwaysTemplate).resizableImage(withCapInsets: UIEdgeInsets(top: 10.0, left: 0.0, bottom: 10.0, right: 0.0)) - - self.zoneView.image = UIImage() - self.zoneView.isUserInteractionEnabled = true - self.zoneView.hitTestSlop = UIEdgeInsets(top: -8.0, left: 0.0, bottom: -8.0, right: 0.0) - - self.leftHandleView.image = handleImage - self.leftHandleView.isUserInteractionEnabled = true - self.leftHandleView.tintColor = .white - self.leftHandleView.contentMode = .scaleToFill - self.leftHandleView.hitTestSlop = UIEdgeInsets(top: -8.0, left: -9.0, bottom: -8.0, right: -9.0) - - self.rightHandleView.image = handleImage - self.rightHandleView.transform = CGAffineTransform(scaleX: -1.0, y: 1.0) - self.rightHandleView.isUserInteractionEnabled = true - self.rightHandleView.tintColor = .white - self.rightHandleView.contentMode = .scaleToFill - self.rightHandleView.hitTestSlop = UIEdgeInsets(top: -8.0, left: -9.0, bottom: -8.0, right: -9.0) - - self.borderView.image = generateImage(CGSize(width: 1.0, height: height), rotatedContext: { size, context in - context.clear(CGRect(origin: .zero, size: size)) - context.setFillColor(UIColor.white.cgColor) - context.fill(CGRect(origin: .zero, size: CGSize(width: size.width, height: borderHeight))) - context.fill(CGRect(origin: CGPoint(x: 0.0, y: size.height - borderHeight), size: CGSize(width: size.width, height: height))) - })?.withRenderingMode(.alwaysTemplate).resizableImage(withCapInsets: UIEdgeInsets(top: 10.0, left: 0.0, bottom: 10.0, right: 0.0)) - self.borderView.tintColor = .white - self.borderView.isUserInteractionEnabled = false - - self.leftCapsuleView.clipsToBounds = true - self.leftCapsuleView.layer.cornerRadius = 1.0 - self.leftCapsuleView.backgroundColor = UIColor(rgb: 0x343436) - - self.rightCapsuleView.clipsToBounds = true - self.rightCapsuleView.layer.cornerRadius = 1.0 - self.rightCapsuleView.backgroundColor = UIColor(rgb: 0x343436) - - self.addSubview(self.zoneView) - self.addSubview(self.leftHandleView) - self.leftHandleView.addSubview(self.leftCapsuleView) - - self.addSubview(self.rightHandleView) - self.rightHandleView.addSubview(self.rightCapsuleView) - self.addSubview(self.borderView) - - self.zoneView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handleZoneHandlePan(_:)))) - self.leftHandleView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handleLeftHandlePan(_:)))) - self.rightHandleView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handleRightHandlePan(_:)))) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - @objc private func handleZoneHandlePan(_ gestureRecognizer: UIPanGestureRecognizer) { - guard let params = self.params else { - return - } - let translation = gestureRecognizer.translation(in: self) - - let start = handleWidth / 2.0 - let end = self.frame.width - handleWidth / 2.0 - let length = end - start - - let delta = translation.x / length - - let duration = params.endPosition - params.startPosition - let startValue = max(0.0, min(params.duration - duration, params.startPosition + delta * params.duration)) - let endValue = startValue + duration - - var transition: Transition = .immediate - switch gestureRecognizer.state { - case .began, .changed: - self.isPanningTrimHandle = true - self.trimUpdated(startValue, endValue, false, false) - if case .began = gestureRecognizer.state { - transition = .easeInOut(duration: 0.25) - } - case .ended, .cancelled: - self.isPanningTrimHandle = false - self.trimUpdated(startValue, endValue, false, true) - transition = .easeInOut(duration: 0.25) - default: - break - } - - gestureRecognizer.setTranslation(.zero, in: self) - self.updated(transition) - } - - @objc private func handleLeftHandlePan(_ gestureRecognizer: UIPanGestureRecognizer) { - guard let params = self.params else { - return - } - let location = gestureRecognizer.location(in: self) - let start = handleWidth / 2.0 - let end = self.frame.width - handleWidth / 2.0 - let length = end - start - let fraction = (location.x - start) / length - - var startValue = max(0.0, params.duration * fraction) - if startValue > params.endPosition - params.minDuration { - startValue = max(0.0, params.endPosition - params.minDuration) - } - var endValue = params.endPosition - if endValue - startValue > params.maxDuration { - let delta = (endValue - startValue) - params.maxDuration - endValue -= delta - } - - var transition: Transition = .immediate - switch gestureRecognizer.state { - case .began, .changed: - self.isPanningTrimHandle = true - self.trimUpdated(startValue, endValue, false, false) - if case .began = gestureRecognizer.state { - transition = .easeInOut(duration: 0.25) - } - case .ended, .cancelled: - self.isPanningTrimHandle = false - self.trimUpdated(startValue, endValue, false, true) - transition = .easeInOut(duration: 0.25) - default: - break - } - self.updated(transition) - } - - @objc private func handleRightHandlePan(_ gestureRecognizer: UIPanGestureRecognizer) { - guard let params = self.params else { - return - } - let location = gestureRecognizer.location(in: self) - let start = handleWidth / 2.0 - let end = self.frame.width - handleWidth / 2.0 - let length = end - start - let fraction = (location.x - start) / length - - var endValue = min(params.duration, params.duration * fraction) - if endValue < params.startPosition + params.minDuration { - endValue = min(params.duration, params.startPosition + params.minDuration) - } - var startValue = params.startPosition - if endValue - startValue > params.maxDuration { - let delta = (endValue - startValue) - params.maxDuration - startValue += delta - } - - var transition: Transition = .immediate - switch gestureRecognizer.state { - case .began, .changed: - self.isPanningTrimHandle = true - self.trimUpdated(startValue, endValue, true, false) - if case .began = gestureRecognizer.state { - transition = .easeInOut(duration: 0.25) - } - case .ended, .cancelled: - self.isPanningTrimHandle = false - self.trimUpdated(startValue, endValue, true, true) - transition = .easeInOut(duration: 0.25) - default: - break - } - self.updated(transition) - } - - var params: ( - duration: Double, - startPosition: Double, - endPosition: Double, - position: Double, - minDuration: Double, - maxDuration: Double - )? - - func update( - totalWidth: CGFloat, - visualInsets: UIEdgeInsets, - scrubberSize: CGSize, - duration: Double, - startPosition: Double, - endPosition: Double, - position: Double, - minDuration: Double, - maxDuration: Double, - transition: Transition - ) -> (leftHandleFrame: CGRect, rightHandleFrame: CGRect) - { - self.params = (duration, startPosition, endPosition, position, minDuration, maxDuration) - - let trimColor = self.isPanningTrimHandle ? UIColor(rgb: 0xf8d74a) : .white - transition.setTintColor(view: self.leftHandleView, color: trimColor) - transition.setTintColor(view: self.rightHandleView, color: trimColor) - transition.setTintColor(view: self.borderView, color: trimColor) - - let leftHandlePositionFraction = duration > 0.0 ? startPosition / duration : 0.0 - let leftHandlePosition = floorToScreenPixels(handleWidth / 2.0 + totalWidth * leftHandlePositionFraction) - - var leftHandleFrame = CGRect(origin: CGPoint(x: leftHandlePosition - handleWidth / 2.0, y: 0.0), size: CGSize(width: handleWidth, height: scrubberSize.height)) - leftHandleFrame.origin.x = max(leftHandleFrame.origin.x, visualInsets.left) - transition.setFrame(view: self.leftHandleView, frame: leftHandleFrame) - - let rightHandlePositionFraction = duration > 0.0 ? endPosition / duration : 1.0 - let rightHandlePosition = floorToScreenPixels(handleWidth / 2.0 + totalWidth * rightHandlePositionFraction) - - var rightHandleFrame = CGRect(origin: CGPoint(x: max(leftHandleFrame.maxX, rightHandlePosition - handleWidth / 2.0), y: 0.0), size: CGSize(width: handleWidth, height: scrubberSize.height)) - rightHandleFrame.origin.x = min(rightHandleFrame.origin.x, totalWidth - visualInsets.right) - transition.setFrame(view: self.rightHandleView, frame: rightHandleFrame) - - let capsuleSize = CGSize(width: 2.0, height: 11.0) - transition.setFrame(view: self.leftCapsuleView, frame: CGRect(origin: CGPoint(x: 5.0 - UIScreenPixel, y: floorToScreenPixels((leftHandleFrame.height - capsuleSize.height) / 2.0)), size: capsuleSize)) - transition.setFrame(view: self.rightCapsuleView, frame: CGRect(origin: CGPoint(x: 5.0 - UIScreenPixel, y: floorToScreenPixels((leftHandleFrame.height - capsuleSize.height) / 2.0)), size: capsuleSize)) - - let zoneFrame = CGRect(x: leftHandleFrame.maxX, y: 0.0, width: rightHandleFrame.minX - leftHandleFrame.maxX, height: scrubberSize.height) - transition.setFrame(view: self.zoneView, frame: zoneFrame) - - let borderFrame = CGRect(origin: CGPoint(x: leftHandleFrame.maxX, y: 0.0), size: CGSize(width: rightHandleFrame.minX - leftHandleFrame.maxX, height: scrubberSize.height)) - transition.setFrame(view: self.borderView, frame: borderFrame) - - return (leftHandleFrame, rightHandleFrame) - } - - override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { - let leftHandleFrame = self.leftHandleView.frame.insetBy(dx: -8.0, dy: -9.0) - let rightHandleFrame = self.rightHandleView.frame.insetBy(dx: -8.0, dy: -9.0) - let areaFrame = CGRect(x: leftHandleFrame.minX, y: leftHandleFrame.minY, width: rightHandleFrame.maxX - leftHandleFrame.minX, height: rightHandleFrame.maxY - rightHandleFrame.minY) - - if self.isHollow { - return leftHandleFrame.contains(point) || rightHandleFrame.contains(point) - } else { - return areaFrame.contains(point) - } - } -} diff --git a/submodules/TelegramUI/Components/MessageInputActionButtonComponent/Sources/MessageInputActionButtonComponent.swift b/submodules/TelegramUI/Components/MessageInputActionButtonComponent/Sources/MessageInputActionButtonComponent.swift index e368bdfdc3d..9698490cdf1 100644 --- a/submodules/TelegramUI/Components/MessageInputActionButtonComponent/Sources/MessageInputActionButtonComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputActionButtonComponent/Sources/MessageInputActionButtonComponent.swift @@ -12,17 +12,104 @@ import ContextUI import ReactionButtonListComponent import TelegramCore +private class ButtonIcon: Equatable { + enum IconType: Equatable { + case none + case send + case apply + case removeVideoInput + case delete + case attach + case forward + case like + case repost + } + + let icon: IconType + + init(icon: IconType) { + self.icon = icon + } + + var image: UIImage? { + switch icon { + case .delete: + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: .white) + case .attach: + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Text/IconAttachment"), color: .white) + case .forward: + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Text/IconForwardSend"), color: .white) + case .like: + return generateTintedImage(image: UIImage(bundleImageName: "Stories/InputLikeOff"), color: .white) + case .removeVideoInput: + return generateTintedImage(image: UIImage(bundleImageName: "Media Editor/RemoveRecordedVideo"), color: .white) + case .repost: + return generateTintedImage(image: UIImage(bundleImageName: "Stories/InputRepost"), color: .white) + case .apply: + return generateImage(CGSize(width: 33.0, height: 33.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(UIColor.white.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) + + if let image = UIImage(bundleImageName: "Media Editor/Apply"), let cgImage = image.cgImage { + context.setBlendMode(.copy) + context.setFillColor(UIColor(rgb: 0x000000, alpha: 0.35).cgColor) + context.clip(to: CGRect(origin: CGPoint(x: -4.0 + UIScreenPixel, y: -3.0 - UIScreenPixel), size: CGSize(width: 40.0, height: 40.0)), mask: cgImage) + context.fill(CGRect(origin: .zero, size: size)) + } + }) + case .send: + return generateImage(CGSize(width: 33.0, height: 33.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(UIColor.white.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) + context.setBlendMode(.copy) + context.setStrokeColor(UIColor.clear.cgColor) + context.setLineWidth(2.0) + context.setLineCap(.round) + context.setLineJoin(.round) + + context.translateBy(x: 5.45, y: 4.0) + + context.saveGState() + context.translateBy(x: 4.0, y: 4.0) + let _ = try? drawSvgPath(context, path: "M1,7 L7,1 L13,7 S ") + context.restoreGState() + + context.saveGState() + context.translateBy(x: 10.0, y: 4.0) + let _ = try? drawSvgPath(context, path: "M1,16 V1 S ") + context.restoreGState() + }) + case .none: + return nil + } + } + + static func == (lhs: ButtonIcon, rhs: ButtonIcon) -> Bool { + return lhs.icon == rhs.icon + } +} + private extension MessageInputActionButtonComponent.Mode { - var iconName: String? { + var icon: ButtonIcon? { switch self { case .delete: - return "Chat/Context Menu/Delete" + return ButtonIcon(icon: .delete) case .attach: - return "Chat/Input/Text/IconAttachment" + return ButtonIcon(icon: .attach) case .forward: - return "Chat/Input/Text/IconForwardSend" + return ButtonIcon(icon: .forward) case .like: - return "Stories/InputLikeOff" + return ButtonIcon(icon: .like) + case .removeVideoInput: + return ButtonIcon(icon: .removeVideoInput) + case .repost: + return ButtonIcon(icon: .repost) + case .apply: + return ButtonIcon(icon: .apply) + case .send: + return ButtonIcon(icon: .send) default: return nil } @@ -36,12 +123,14 @@ public final class MessageInputActionButtonComponent: Component { case apply case voiceInput case videoInput + case removeVideoInput case unavailableVoiceInput case delete case attach case forward case more case like(reaction: MessageReaction.Reaction?, file: TelegramMediaFile?, animationFileId: Int64?) + case repost } public enum Action { @@ -64,6 +153,7 @@ public final class MessageInputActionButtonComponent: Component { public let presentController: (ViewController) -> Void public let audioRecorder: ManagedAudioRecorder? public let videoRecordingStatus: InstantVideoControllerRecordingStatus? + public let hasShadow: Bool public init( mode: Mode, @@ -80,7 +170,8 @@ public final class MessageInputActionButtonComponent: Component { strings: PresentationStrings, presentController: @escaping (ViewController) -> Void, audioRecorder: ManagedAudioRecorder?, - videoRecordingStatus: InstantVideoControllerRecordingStatus? + videoRecordingStatus: InstantVideoControllerRecordingStatus?, + hasShadow: Bool = false ) { self.mode = mode self.storyId = storyId @@ -97,6 +188,7 @@ public final class MessageInputActionButtonComponent: Component { self.presentController = presentController self.audioRecorder = audioRecorder self.videoRecordingStatus = videoRecordingStatus + self.hasShadow = hasShadow } public static func ==(lhs: MessageInputActionButtonComponent, rhs: MessageInputActionButtonComponent) -> Bool { @@ -121,6 +213,9 @@ public final class MessageInputActionButtonComponent: Component { if lhs.videoRecordingStatus !== rhs.videoRecordingStatus { return false } + if lhs.hasShadow != rhs.hasShadow { + return false + } return true } @@ -214,12 +309,7 @@ public final class MessageInputActionButtonComponent: Component { } component.action(component.mode, .up, false) } - -// public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { -// let result = super.hitTest(point, with: event) -// return result -// } - + func update(component: MessageInputActionButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { let previousComponent = self.component self.component = component @@ -333,7 +423,7 @@ public final class MessageInputActionButtonComponent: Component { switch component.mode { case .none: break - case .send, .apply, .attach, .delete, .forward: + case .send, .apply, .attach, .delete, .forward, .removeVideoInput, .repost: sendAlpha = 1.0 case let .like(reaction, _, _): if reaction != nil { @@ -349,63 +439,37 @@ public final class MessageInputActionButtonComponent: Component { microphoneAlpha = 0.4 } - if self.sendIconView.image == nil || previousComponent?.mode.iconName != component.mode.iconName { - if let iconName = component.mode.iconName { - let tintColor: UIColor = .white - self.sendIconView.image = generateTintedImage(image: UIImage(bundleImageName: iconName), color: tintColor) - } else if case .apply = component.mode { - self.sendIconView.image = generateImage(CGSize(width: 33.0, height: 33.0), contextGenerator: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - context.setFillColor(UIColor.white.cgColor) - context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) - - if let image = UIImage(bundleImageName: "Media Editor/Apply"), let cgImage = image.cgImage { - context.setBlendMode(.copy) - context.setFillColor(UIColor(rgb: 0x000000, alpha: 0.35).cgColor) - context.clip(to: CGRect(origin: CGPoint(x: -4.0 + UIScreenPixel, y: -3.0 - UIScreenPixel), size: CGSize(width: 40.0, height: 40.0)), mask: cgImage) - context.fill(CGRect(origin: .zero, size: size)) + if self.sendIconView.image == nil || previousComponent?.mode.icon != component.mode.icon { + if let image = component.mode.icon?.image { + if case .send = component.mode { + if !transition.animation.isImmediate { + if let snapshotView = self.sendIconView.snapshotView(afterScreenUpdates: false) { + snapshotView.frame = self.sendIconView.frame + self.addSubview(snapshotView) + + transition.setAlpha(view: snapshotView, alpha: 0.0, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + transition.setScale(view: snapshotView, scale: 0.01) + + self.sendIconView.alpha = 0.0 + transition.animateAlpha(view: self.sendIconView, from: 0.0, to: sendAlpha) + transition.animateScale(view: self.sendIconView, from: 0.01, to: 1.0) + } } - }) - } else if case .none = component.mode { + } + self.sendIconView.image = image + } else { self.sendIconView.image = nil + } + if case .removeVideoInput = component.mode, component.hasShadow { + self.sendIconView.layer.shadowOffset = CGSize(width: 0.0, height: 0.0) + self.sendIconView.layer.shadowRadius = 2.0 + self.sendIconView.layer.shadowColor = UIColor.black.cgColor + self.sendIconView.layer.shadowOpacity = 0.35 } else { - if !transition.animation.isImmediate { - if let snapshotView = self.sendIconView.snapshotView(afterScreenUpdates: false) { - snapshotView.frame = self.sendIconView.frame - self.addSubview(snapshotView) - - transition.setAlpha(view: snapshotView, alpha: 0.0, completion: { [weak snapshotView] _ in - snapshotView?.removeFromSuperview() - }) - transition.setScale(view: snapshotView, scale: 0.01) - - self.sendIconView.alpha = 0.0 - transition.animateAlpha(view: self.sendIconView, from: 0.0, to: sendAlpha) - transition.animateScale(view: self.sendIconView, from: 0.01, to: 1.0) - } - } - self.sendIconView.image = generateImage(CGSize(width: 33.0, height: 33.0), rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - context.setFillColor(UIColor.white.cgColor) - context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) - context.setBlendMode(.copy) - context.setStrokeColor(UIColor.clear.cgColor) - context.setLineWidth(2.0) - context.setLineCap(.round) - context.setLineJoin(.round) - - context.translateBy(x: 5.45, y: 4.0) - - context.saveGState() - context.translateBy(x: 4.0, y: 4.0) - let _ = try? drawSvgPath(context, path: "M1,7 L7,1 L13,7 S ") - context.restoreGState() - - context.saveGState() - context.translateBy(x: 10.0, y: 4.0) - let _ = try? drawSvgPath(context, path: "M1,16 V1 S ") - context.restoreGState() - }) + self.sendIconView.layer.shadowColor = UIColor.clear.cgColor + self.sendIconView.layer.shadowOpacity = 0.0 } } @@ -534,6 +598,9 @@ public final class MessageInputActionButtonComponent: Component { } if let micButton = self.micButton { + micButton.hasShadow = component.hasShadow + micButton.hidesOnLock = component.hasShadow + if themeUpdated { micButton.updateTheme(theme: defaultDarkPresentationTheme) } @@ -548,9 +615,9 @@ public final class MessageInputActionButtonComponent: Component { if previousComponent?.mode != component.mode { switch component.mode { - case .none, .send, .apply, .voiceInput, .attach, .delete, .forward, .unavailableVoiceInput, .more, .like: + case .none, .send, .apply, .voiceInput, .attach, .delete, .forward, .unavailableVoiceInput, .more, .like, .repost: micButton.updateMode(mode: .audio, animated: !transition.animation.isImmediate) - case .videoInput: + case .videoInput, .removeVideoInput: micButton.updateMode(mode: .video, animated: !transition.animation.isImmediate) } } diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/BUILD b/submodules/TelegramUI/Components/MessageInputPanelComponent/BUILD index 225d0f982ea..dec402da52a 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/BUILD +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/BUILD @@ -39,6 +39,7 @@ swift_library( "//submodules/SearchPeerMembers", "//submodules/ContextUI", "//submodules/TelegramUI/Components/ContextReferenceButtonComponent", + "//submodules/TelegramUI/Components/Stories/ForwardInfoPanelComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift index 0edc572a298..19a8992f9a9 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift @@ -19,6 +19,7 @@ import AnimatedTextComponent import AnimatedCountLabelNode import MessageInputActionButtonComponent import ContextReferenceButtonComponent +import ForwardInfoPanelComponent private var sharedIsReduceTransparencyEnabled = UIAccessibility.isReduceTransparencyEnabled @@ -116,7 +117,7 @@ public final class MessageInputPanelComponent: Component { public let sendMessageAction: () -> Void public let sendMessageOptionsAction: ((UIView, ContextGesture?) -> Void)? public let sendStickerAction: (TelegramMediaFile) -> Void - public let setMediaRecordingActive: ((Bool, Bool, Bool) -> Void)? + public let setMediaRecordingActive: ((Bool, Bool, Bool, UIView?) -> Void)? public let lockMediaRecording: (() -> Void)? public let stopAndPreviewMediaRecording: (() -> Void)? public let discardMediaRecordingPreview: (() -> Void)? @@ -135,6 +136,7 @@ public final class MessageInputPanelComponent: Component { public let audioRecorder: ManagedAudioRecorder? public let videoRecordingStatus: InstantVideoControllerRecordingStatus? public let isRecordingLocked: Bool + public let hasRecordedVideo: Bool public let recordedAudioPreview: ChatRecordedMediaPreview? public let hasRecordedVideoPreview: Bool public let wasRecordingDismissed: Bool @@ -147,6 +149,7 @@ public final class MessageInputPanelComponent: Component { public let customInputView: UIView? public let forceIsEditing: Bool public let disabledPlaceholder: String? + public let header: AnyComponent? public let isChannel: Bool public let storyItem: EngineStoryItem? public let chatLocation: ChatLocation? @@ -169,7 +172,7 @@ public final class MessageInputPanelComponent: Component { sendMessageAction: @escaping () -> Void, sendMessageOptionsAction: ((UIView, ContextGesture?) -> Void)?, sendStickerAction: @escaping (TelegramMediaFile) -> Void, - setMediaRecordingActive: ((Bool, Bool, Bool) -> Void)?, + setMediaRecordingActive: ((Bool, Bool, Bool, UIView?) -> Void)?, lockMediaRecording: (() -> Void)?, stopAndPreviewMediaRecording: (() -> Void)?, discardMediaRecordingPreview: (() -> Void)?, @@ -188,6 +191,7 @@ public final class MessageInputPanelComponent: Component { audioRecorder: ManagedAudioRecorder?, videoRecordingStatus: InstantVideoControllerRecordingStatus?, isRecordingLocked: Bool, + hasRecordedVideo: Bool, recordedAudioPreview: ChatRecordedMediaPreview?, hasRecordedVideoPreview: Bool, wasRecordingDismissed: Bool, @@ -200,6 +204,7 @@ public final class MessageInputPanelComponent: Component { customInputView: UIView?, forceIsEditing: Bool, disabledPlaceholder: String?, + header: AnyComponent?, isChannel: Bool, storyItem: EngineStoryItem?, chatLocation: ChatLocation? @@ -240,6 +245,7 @@ public final class MessageInputPanelComponent: Component { self.audioRecorder = audioRecorder self.videoRecordingStatus = videoRecordingStatus self.isRecordingLocked = isRecordingLocked + self.hasRecordedVideo = hasRecordedVideo self.wasRecordingDismissed = wasRecordingDismissed self.recordedAudioPreview = recordedAudioPreview self.hasRecordedVideoPreview = hasRecordedVideoPreview @@ -252,6 +258,7 @@ public final class MessageInputPanelComponent: Component { self.customInputView = customInputView self.forceIsEditing = forceIsEditing self.disabledPlaceholder = disabledPlaceholder + self.header = header self.isChannel = isChannel self.storyItem = storyItem self.chatLocation = chatLocation @@ -300,6 +307,9 @@ public final class MessageInputPanelComponent: Component { if lhs.isRecordingLocked != rhs.isRecordingLocked { return false } + if lhs.hasRecordedVideo != rhs.hasRecordedVideo { + return false + } if lhs.wasRecordingDismissed != rhs.wasRecordingDismissed { return false } @@ -330,6 +340,9 @@ public final class MessageInputPanelComponent: Component { if (lhs.moreAction == nil) != (rhs.moreAction == nil) { return false } + if (lhs.setMediaRecordingActive == nil) != (rhs.setMediaRecordingActive == nil) { + return false + } if lhs.hideKeyboard != rhs.hideKeyboard { return false } @@ -342,6 +355,9 @@ public final class MessageInputPanelComponent: Component { if lhs.disabledPlaceholder != rhs.disabledPlaceholder { return false } + if lhs.header != rhs.header { + return false + } if (lhs.attachmentAction == nil) != (rhs.attachmentAction == nil) { return false } @@ -380,6 +396,7 @@ public final class MessageInputPanelComponent: Component { private let vibrancyPlaceholder = ComponentView() private let counter = ComponentView() + private var header: ComponentView? private var disabledPlaceholder: ComponentView? private var textClippingView = UIView() @@ -436,12 +453,7 @@ public final class MessageInputPanelComponent: Component { override init(frame: CGRect) { self.fieldBackgroundView = BlurredBackgroundView(color: UIColor(white: 0.0, alpha: 0.5), enableBlur: true) - let style: UIBlurEffect.Style = .dark - let blurEffect = UIBlurEffect(style: style) - let vibrancyEffect = UIVibrancyEffect(blurEffect: blurEffect) - let vibrancyEffectView = UIVisualEffectView(effect: vibrancyEffect) - vibrancyEffectView.alpha = 0.0 - self.vibrancyEffectView = vibrancyEffectView + self.vibrancyEffectView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .dark))) self.mediaRecordingVibrancyContainer = UIView() self.vibrancyEffectView.contentView.addSubview(self.mediaRecordingVibrancyContainer) @@ -786,11 +798,67 @@ public final class MessageInputPanelComponent: Component { insets.right = insets.left } - let fieldFrame = CGRect(origin: CGPoint(x: insets.left, y: insets.top), size: CGSize(width: availableSize.width - insets.left - insets.right, height: textFieldSize.height)) + var headerHeight: CGFloat = 0.0 + if let headerComponent = component.header, !hasMediaRecording && !hasMediaEditing { + let headerInset: CGFloat = 10.0 + let header: ComponentView + var headerTransition = transition + if let current = self.header { + header = current + } else { + headerTransition = .immediate + header = ComponentView() + self.header = header + } + let headerSize = header.update( + transition: .immediate, + component: headerComponent, + environment: {}, + containerSize: CGSize(width: availableSize.width - insets.left - insets.right - headerInset * 2.0, height: 100.0) + ) + let headerFrame = CGRect(origin: CGPoint(x: insets.left + headerInset, y: insets.top + headerInset), size: headerSize) + if let headerView = header.view { + if let headerView = headerView as? ForwardInfoPanelComponent.View { + if headerView.superview == nil { + self.addSubview(headerView) + self.vibrancyEffectView.contentView.addSubview(headerView.backgroundView) + + headerView.backgroundView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) + } + headerView.backgroundView.frame = headerFrame.offsetBy(dx: -9.0, dy: -14.0) + } else { + if headerView.superview == nil { + self.addSubview(headerView) + } + } + headerTransition.setPosition(view: headerView, position: headerFrame.center) + headerView.bounds = CGRect(origin: CGPoint(), size: headerFrame.size) + } + headerHeight = headerFrame.height + headerInset + } else { + if let header = self.header { + self.header = nil + if let headerView = header.view as? ForwardInfoPanelComponent.View { + headerView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + headerView.removeFromSuperview() + }) + headerView.backgroundView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + headerView.backgroundView.removeFromSuperview() + }) + } else { + header.view?.removeFromSuperview() + } + } + } + + var fieldFrame = CGRect(origin: CGPoint(x: insets.left, y: insets.top), size: CGSize(width: availableSize.width - insets.left - insets.right, height: textFieldSize.height)) + if hasMediaRecording || hasMediaEditing { + fieldFrame.size.height = baseFieldHeight + } var fieldBackgroundFrame: CGRect if hasMediaRecording { - fieldBackgroundFrame = CGRect(origin: CGPoint(x: mediaInsets.left, y: insets.top), size: CGSize(width: availableSize.width - mediaInsets.left - mediaInsets.right, height: textFieldSize.height)) + fieldBackgroundFrame = CGRect(origin: CGPoint(x: mediaInsets.left, y: insets.top), size: CGSize(width: availableSize.width - mediaInsets.left - mediaInsets.right, height: fieldFrame.height)) } else if isEditing || component.style == .editor || component.style == .media { fieldBackgroundFrame = fieldFrame } else { @@ -802,17 +870,17 @@ public final class MessageInputPanelComponent: Component { fieldBackgroundFrame = CGRect(origin: CGPoint(x: mediaInsets.left, y: insets.top), size: CGSize(width: availableSize.width - mediaInsets.left - 50.0, height: textFieldSize.height)) } } + + let rawFieldBackgroundFrame = fieldBackgroundFrame + fieldBackgroundFrame.size.height += headerHeight transition.setFrame(view: self.vibrancyEffectView, frame: CGRect(origin: CGPoint(), size: fieldBackgroundFrame.size)) self.vibrancyEffectView.isHidden = component.style == .media - if isEditing { - self.vibrancyEffectView.alpha = 1.0 - } transition.setFrame(view: self.fieldBackgroundView, frame: fieldBackgroundFrame) - self.fieldBackgroundView.update(size: fieldBackgroundFrame.size, cornerRadius: baseFieldHeight * 0.5, transition: transition.containedViewLayoutTransition) + self.fieldBackgroundView.update(size: fieldBackgroundFrame.size, cornerRadius: headerHeight > 0.0 ? 18.0 : baseFieldHeight * 0.5, transition: transition.containedViewLayoutTransition) - var textClippingFrame = fieldBackgroundFrame + var textClippingFrame = rawFieldBackgroundFrame.offsetBy(dx: 0.0, dy: headerHeight) if component.style == .media, !isEditing { textClippingFrame.size.height -= 10.0 } @@ -828,13 +896,15 @@ public final class MessageInputPanelComponent: Component { if isEditing || component.style == .story { placeholderOriginX = 16.0 } else { - placeholderOriginX = floorToScreenPixels((availableSize.width - placeholderSize.width) / 2.0) + placeholderOriginX = floorToScreenPixels(fieldBackgroundFrame.minX + (fieldBackgroundFrame.width - placeholderSize.width) / 2.0) } - let placeholderFrame = CGRect(origin: CGPoint(x: placeholderOriginX, y: floor((fieldBackgroundFrame.height - placeholderSize.height) * 0.5)), size: placeholderSize) + let placeholderFrame = CGRect(origin: CGPoint(x: placeholderOriginX, y: headerHeight + floor((rawFieldBackgroundFrame.height - placeholderSize.height) * 0.5)), size: placeholderSize) if let placeholderView = self.placeholder.view, let vibrancyPlaceholderView = self.vibrancyPlaceholder.view { if vibrancyPlaceholderView.superview == nil { vibrancyPlaceholderView.layer.anchorPoint = CGPoint() self.vibrancyEffectView.contentView.addSubview(vibrancyPlaceholderView) + + vibrancyPlaceholderView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) } transition.setPosition(view: vibrancyPlaceholderView, position: placeholderFrame.origin) vibrancyPlaceholderView.bounds = CGRect(origin: CGPoint(), size: placeholderFrame.size) @@ -854,7 +924,10 @@ public final class MessageInputPanelComponent: Component { let fieldAlpha = sharedIsReduceTransparencyEnabled ? 0.09 : 1.0 transition.setAlpha(view: self.fieldBackgroundView, alpha: (component.disabledPlaceholder != nil || component.isChannel) ? 0.0 : fieldAlpha) - let size = CGSize(width: availableSize.width, height: textFieldSize.height + insets.top + insets.bottom) + var size = CGSize(width: availableSize.width, height: textFieldSize.height + insets.top + insets.bottom + headerHeight) + if hasMediaRecording || hasMediaEditing { + size.height = baseFieldHeight + insets.top + insets.bottom + } var rightButtonsOffsetX: CGFloat = 0.0 if component.isChannel, let storyItem = component.storyItem { @@ -1157,7 +1230,11 @@ public final class MessageInputPanelComponent: Component { let inputActionButtonMode: MessageInputActionButtonComponent.Mode if case .editor = component.style { - inputActionButtonMode = isEditing ? .apply : .none + if isEditing { + inputActionButtonMode = .apply + } else { + inputActionButtonMode = component.hasRecordedVideo ? .removeVideoInput : .videoInput + } } else if case .media = component.style { inputActionButtonMode = isEditing ? .apply : .none } else { @@ -1216,7 +1293,11 @@ public final class MessageInputPanelComponent: Component { } } case .voiceInput, .videoInput: - component.setMediaRecordingActive?(action == .down, mode == .videoInput, sendAction) + component.setMediaRecordingActive?(action == .down, mode == .videoInput, sendAction, self.inputActionButton.view) + case .removeVideoInput: + if case .up = action { + component.setMediaRecordingActive?(true, true, false, nil) + } case .forward: if case .up = action { component.forwardAction?() @@ -1266,7 +1347,8 @@ public final class MessageInputPanelComponent: Component { strings: component.strings, presentController: component.presentController, audioRecorder: component.audioRecorder, - videoRecordingStatus: component.videoRecordingStatus + videoRecordingStatus: component.videoRecordingStatus, + hasShadow: component.style == .editor )), environment: {}, containerSize: CGSize(width: 33.0, height: 33.0) @@ -1463,7 +1545,7 @@ public final class MessageInputPanelComponent: Component { if stickerButtonView.superview == nil { self.addSubview(stickerButtonView) } - let stickerIconFrame = CGRect(origin: CGPoint(x: fieldIconNextX - stickerButtonSize.width, y: fieldFrame.maxY - 4.0 - stickerButtonSize.height), size: stickerButtonSize) + let stickerIconFrame = CGRect(origin: CGPoint(x: fieldIconNextX - stickerButtonSize.width, y: fieldBackgroundFrame.maxY - 4.0 - stickerButtonSize.height), size: stickerButtonSize) transition.setPosition(view: stickerButtonView, position: stickerIconFrame.center) transition.setBounds(view: stickerButtonView, bounds: CGRect(origin: CGPoint(), size: stickerIconFrame.size)) @@ -1508,12 +1590,13 @@ public final class MessageInputPanelComponent: Component { self.addSubview(timeoutButtonView) } let originX = fieldBackgroundFrame.maxX - 4.0 - let timeoutIconFrame = CGRect(origin: CGPoint(x: originX - timeoutButtonSize.width, y: fieldFrame.maxY - 4.0 - timeoutButtonSize.height), size: timeoutButtonSize) + let timeoutIconFrame = CGRect(origin: CGPoint(x: originX - timeoutButtonSize.width, y: fieldBackgroundFrame.maxY - 4.0 - timeoutButtonSize.height), size: timeoutButtonSize) transition.setPosition(view: timeoutButtonView, position: timeoutIconFrame.center) transition.setBounds(view: timeoutButtonView, bounds: CGRect(origin: CGPoint(), size: timeoutIconFrame.size)) - transition.setAlpha(view: timeoutButtonView, alpha: isEditing ? 0.0 : 1.0) - transition.setScale(view: timeoutButtonView, scale: isEditing ? 0.1 : 1.0) + let visible = !hasMediaRecording && !hasMediaEditing && !isEditing + transition.setAlpha(view: timeoutButtonView, alpha: visible ? 1.0 : 0.0) + transition.setScale(view: timeoutButtonView, scale: visible ? 1.0 : 0.1) } } @@ -1578,7 +1661,7 @@ public final class MessageInputPanelComponent: Component { guard let self, let component = self.component else { return } - component.setMediaRecordingActive?(false, false, false) + component.setMediaRecordingActive?(false, false, false, nil) } )), environment: {}, @@ -1588,7 +1671,7 @@ public final class MessageInputPanelComponent: Component { var animateIn = false if mediaRecordingPanelView.superview == nil { animateIn = true - self.insertSubview(mediaRecordingPanelView, aboveSubview: self.fieldBackgroundView) + self.insertSubview(mediaRecordingPanelView, aboveSubview: self.textClippingView) self.mediaRecordingVibrancyContainer.addSubview(mediaRecordingPanelView.vibrancyContainer) } diff --git a/submodules/TelegramUI/Components/MultiScaleTextNode/BUILD b/submodules/TelegramUI/Components/MultiScaleTextNode/BUILD new file mode 100644 index 00000000000..1885ed1df94 --- /dev/null +++ b/submodules/TelegramUI/Components/MultiScaleTextNode/BUILD @@ -0,0 +1,19 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "MultiScaleTextNode", + module_name = "MultiScaleTextNode", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/AsyncDisplayKit", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/MultiScaleTextNode/Sources/MultiScaleTextNode.swift b/submodules/TelegramUI/Components/MultiScaleTextNode/Sources/MultiScaleTextNode.swift new file mode 100644 index 00000000000..3592a6eb4d2 --- /dev/null +++ b/submodules/TelegramUI/Components/MultiScaleTextNode/Sources/MultiScaleTextNode.swift @@ -0,0 +1,146 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display + +private final class MultiScaleTextStateNode: ASDisplayNode { + let tintTextNode: ImmediateTextNode + let noTintTextNode: ImmediateTextNode + + var currentLayout: MultiScaleTextLayout? + + override init() { + self.tintTextNode = ImmediateTextNode() + self.tintTextNode.displaysAsynchronously = false + self.tintTextNode.renderContentTypes = TextNode.RenderContentTypes.all.subtracting(TextNode.RenderContentTypes.emoji) + + self.noTintTextNode = ImmediateTextNode() + self.noTintTextNode.displaysAsynchronously = false + self.noTintTextNode.renderContentTypes = .emoji + + super.init() + + self.addSubnode(self.tintTextNode) + self.addSubnode(self.noTintTextNode) + } +} + +public final class MultiScaleTextState { + public struct Attributes { + public var font: UIFont + public var color: UIColor + public var shadowColor: UIColor? + + public init(font: UIFont, color: UIColor, shadowColor: UIColor? = nil) { + self.font = font + self.color = color + self.shadowColor = shadowColor + } + } + + public let attributes: Attributes + public let constrainedSize: CGSize + + public init(attributes: Attributes, constrainedSize: CGSize) { + self.attributes = attributes + self.constrainedSize = constrainedSize + } +} + +public struct MultiScaleTextLayout { + public var size: CGSize + + public init(size: CGSize) { + self.size = size + } +} + +public final class MultiScaleTextNode: ASDisplayNode { + private let stateNodes: [AnyHashable: MultiScaleTextStateNode] + + public init(stateKeys: [AnyHashable]) { + self.stateNodes = Dictionary(stateKeys.map { ($0, MultiScaleTextStateNode()) }, uniquingKeysWith: { lhs, _ in lhs }) + + super.init() + + for (_, node) in self.stateNodes { + self.addSubnode(node) + } + } + + public func stateNode(forKey key: AnyHashable) -> ASDisplayNode? { + return self.stateNodes[key]?.tintTextNode + } + + public func updateTintColor(color: UIColor, transition: ContainedViewLayoutTransition) { + for (_, node) in self.stateNodes { + transition.updateTintColor(layer: node.tintTextNode.layer, color: color) + } + } + + public func updateLayout(text: String, states: [AnyHashable: MultiScaleTextState], mainState: AnyHashable) -> [AnyHashable: MultiScaleTextLayout] { + assert(Set(states.keys) == Set(self.stateNodes.keys)) + assert(states[mainState] != nil) + + var result: [AnyHashable: MultiScaleTextLayout] = [:] + var mainLayout: MultiScaleTextLayout? + for (key, state) in states { + if let node = self.stateNodes[key] { + node.tintTextNode.attributedText = NSAttributedString(string: text, attributes: [ + .font: state.attributes.font, + .foregroundColor: state.attributes.color + ]) + node.noTintTextNode.attributedText = NSAttributedString(string: text, attributes: [ + .font: state.attributes.font, + .foregroundColor: state.attributes.color + ]) + if let shadowColor = state.attributes.shadowColor { + node.tintTextNode.textShadowColor = shadowColor + node.tintTextNode.textShadowBlur = 3.0 + node.noTintTextNode.textShadowColor = shadowColor + node.noTintTextNode.textShadowBlur = 3.0 + } else { + node.tintTextNode.shadowColor = nil + node.noTintTextNode.shadowColor = nil + } + node.tintTextNode.isAccessibilityElement = true + node.tintTextNode.accessibilityLabel = text + node.noTintTextNode.isAccessibilityElement = false + let nodeSize = node.tintTextNode.updateLayout(state.constrainedSize) + let _ = node.noTintTextNode.updateLayout(state.constrainedSize) + let nodeLayout = MultiScaleTextLayout(size: nodeSize) + if key == mainState { + mainLayout = nodeLayout + } + node.currentLayout = nodeLayout + result[key] = nodeLayout + } + } + if let mainLayout = mainLayout { + let mainBounds = CGRect(origin: CGPoint(x: -mainLayout.size.width / 2.0, y: -mainLayout.size.height / 2.0), size: mainLayout.size) + for (key, _) in states { + if let node = self.stateNodes[key], let nodeLayout = result[key] { + let textFrame = CGRect(origin: CGPoint(x: mainBounds.minX, y: mainBounds.minY + floor((mainBounds.height - nodeLayout.size.height) / 2.0)), size: nodeLayout.size) + node.tintTextNode.frame = textFrame + node.noTintTextNode.frame = textFrame + } + } + } + return result + } + + public func update(stateFractions: [AnyHashable: CGFloat], alpha: CGFloat = 1.0, transition: ContainedViewLayoutTransition) { + var fractionSum: CGFloat = 0.0 + for (_, fraction) in stateFractions { + fractionSum += fraction + } + for (key, fraction) in stateFractions { + if let node = self.stateNodes[key], let _ = node.currentLayout { + if !transition.isAnimated { + node.layer.removeAllAnimations() + } + transition.updateAlpha(node: node, alpha: fraction / fractionSum * alpha) + } + } + } +} diff --git a/submodules/TelegramUI/Components/NotificationExceptionsScreen/Sources/NotificationExceptionsScreen.swift b/submodules/TelegramUI/Components/NotificationExceptionsScreen/Sources/NotificationExceptionsScreen.swift index 5bb42be0b11..3f7c37cfd70 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)), 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)), value.threadId, value.info, title, value.notificationSettings._asNotificationSettings(), state.editing, state.revealedThreadId == value.threadId)) index += 1 } diff --git a/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/BUILD b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/BUILD new file mode 100644 index 00000000000..631aee7b7de --- /dev/null +++ b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/BUILD @@ -0,0 +1,39 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "PeerAllowedReactionsScreen", + module_name = "PeerAllowedReactionsScreen", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display:Display", + "//submodules/TelegramPresentationData:TelegramPresentationData", + "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/AppBundle", + "//submodules/Components/ViewControllerComponent", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/TelegramCore", + "//submodules/Postbox", + "//submodules/AccountContext", + "//submodules/TelegramUI/Components/EntityKeyboard", + "//submodules/TelegramUI/Components/SwitchComponent", + "//submodules/Components/MultilineTextComponent", + "//submodules/Markdown", + "//submodules/TelegramUI/Components/ButtonComponent", + "//submodules/TelegramUI/Components/AnimatedTextComponent", + "//submodules/Components/BundleIconComponent", + "//submodules/Components/PagerComponent", + "//submodules/PremiumUI", + "//submodules/UndoUI", + "//submodules/TextFormat", + "//submodules/Components/HierarchyTrackingLayer", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/EmojiListInputComponent.swift b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/EmojiListInputComponent.swift new file mode 100644 index 00000000000..1d7a582d772 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/EmojiListInputComponent.swift @@ -0,0 +1,345 @@ +import Foundation +import UIKit +import Display +import TelegramPresentationData +import ComponentFlow +import ComponentDisplayAdapters +import SwitchComponent +import EntityKeyboard +import AccountContext +import HierarchyTrackingLayer + +private final class CaretIndicatorView: UIImageView { + private let hierarchyTrackingLayer: HierarchyTrackingLayer + + override init(frame: CGRect) { + self.hierarchyTrackingLayer = HierarchyTrackingLayer() + + super.init(frame: frame) + + self.layer.addSublayer(self.hierarchyTrackingLayer) + self.hierarchyTrackingLayer.didEnterHierarchy = { [weak self] in + self?.restartAnimations(delayStart: false) + } + } + + required init?(coder: NSCoder) { + preconditionFailure() + } + + func restartAnimations(delayStart: Bool) { + self.layer.removeAnimation(forKey: "caret") + + let animation = CAKeyframeAnimation(keyPath: "opacity") + animation.values = [1.0 as NSNumber, 0.0 as NSNumber, 1.0 as NSNumber, 1.0 as NSNumber] + let firstDuration = 0.3 + let secondDuration = 0.25 + let restDuration = 0.35 + let duration = firstDuration + secondDuration + restDuration + let keyTimes: [NSNumber] = [0.0 as NSNumber, (firstDuration / duration) as NSNumber, ((firstDuration + secondDuration) / duration) as NSNumber, ((firstDuration + secondDuration + restDuration) / duration) as NSNumber] + + animation.keyTimes = keyTimes + animation.duration = duration + animation.repeatCount = Float.greatestFiniteMagnitude + animation.fillMode = .both + if delayStart { + animation.beginTime = self.layer.convertTime(CACurrentMediaTime(), from: nil) + 0.8 * UIView.animationDurationFactor() + } + self.layer.add(animation, forKey: "caret") + } +} + +final class EmojiListInputComponent: Component { + let context: AccountContext + let theme: PresentationTheme + let placeholder: String + let reactionItems: [EmojiComponentReactionItem] + let isInputActive: Bool + let caretPosition: Int + let activateInput: () -> Void + let setCaretPosition: (Int) -> Void + + init( + context: AccountContext, + theme: PresentationTheme, + placeholder: String, + reactionItems: [EmojiComponentReactionItem], + isInputActive: Bool, + caretPosition: Int, + activateInput: @escaping () -> Void, + setCaretPosition: @escaping (Int) -> Void + ) { + self.context = context + self.theme = theme + self.placeholder = placeholder + self.reactionItems = reactionItems + self.isInputActive = isInputActive + self.caretPosition = caretPosition + self.activateInput = activateInput + self.setCaretPosition = setCaretPosition + } + + static func ==(lhs: EmojiListInputComponent, rhs: EmojiListInputComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.placeholder != rhs.placeholder { + return false + } + if lhs.reactionItems != rhs.reactionItems { + return false + } + if lhs.isInputActive != rhs.isInputActive { + return false + } + if lhs.caretPosition != rhs.caretPosition { + return false + } + return true + } + + final class View: UIView { + private var component: EmojiListInputComponent? + private weak var state: EmptyComponentState? + + private var itemLayers: [Int64: EmojiPagerContentComponent.View.ItemLayer] = [:] + private let trailingPlaceholder = ComponentView() + private let caretIndicator: CaretIndicatorView + + override init(frame: CGRect) { + self.caretIndicator = CaretIndicatorView(frame: CGRect()) + self.caretIndicator.image = generateImage(CGSize(width: 2.0, height: 4.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(UIColor.white.cgColor) + context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: size.width * 0.5).cgPath) + context.fillPath() + })?.stretchableImage(withLeftCapWidth: 1, topCapHeight: 2).withRenderingMode(.alwaysTemplate) + + super.init(frame: frame) + + self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) + } + + required init(coder: NSCoder) { + preconditionFailure() + } + + @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { + guard let component = self.component else { + return + } + + if case .ended = recognizer.state { + let point = recognizer.location(in: self) + + var tapOnItem = false + for (itemId, itemLayer) in self.itemLayers { + if itemLayer.frame.insetBy(dx: -6.0, dy: -6.0).contains(point) { + if let itemIndex = component.reactionItems.firstIndex(where: { $0.file.fileId.id == itemId }) { + var caretPosition = point.x >= itemLayer.frame.midX ? (itemIndex + 1) : itemIndex + caretPosition = max(0, min(component.reactionItems.count, caretPosition)) + component.setCaretPosition(caretPosition) + component.activateInput() + } + tapOnItem = true + break + } + } + + if !tapOnItem { + component.setCaretPosition(component.reactionItems.count) + component.activateInput() + } + } + } + + func caretRect() -> CGRect? { + if !self.caretIndicator.isHidden { + return self.caretIndicator.frame + } else { + return nil + } + } + + func update(component: EmojiListInputComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + let verticalInset: CGFloat = 12.0 + let placeholderSpacing: CGFloat = 6.0 + + let minItemSize: CGFloat = 24.0 + let itemSpacingFactor: CGFloat = 0.15 + let minSideInset: CGFloat = 12.0 + + self.backgroundColor = component.theme.list.itemBlocksBackgroundColor + self.layer.cornerRadius = 12.0 + + let maxItemsWidth = availableSize.width - minSideInset * 2.0 + let itemsPerRow = Int(floor((maxItemsWidth + minItemSize * itemSpacingFactor) / (minItemSize + minItemSize * itemSpacingFactor))) + let itemSizePlusSpacing = maxItemsWidth / CGFloat(itemsPerRow) + let itemSize = floor(itemSizePlusSpacing * (1.0 - itemSpacingFactor)) + let itemSpacing = floor(itemSizePlusSpacing * itemSpacingFactor) + let sideInset = floor((availableSize.width - (itemSize * CGFloat(itemsPerRow) + itemSpacing * CGFloat(itemsPerRow - 1))) * 0.5) + + var rowCount = (component.reactionItems.count + (itemsPerRow - 1)) / itemsPerRow + rowCount = max(1, rowCount) + + if let previousComponent = self.component, (previousComponent.reactionItems.count != component.reactionItems.count || previousComponent.isInputActive != component.isInputActive) { + self.caretIndicator.restartAnimations(delayStart: true) + } + + self.component = component + self.state = state + + let trailingPlaceholderSize = self.trailingPlaceholder.update( + transition: .immediate, + component: AnyComponent(Text(text: component.placeholder, font: Font.regular(17.0), color: component.theme.list.itemPlaceholderTextColor)), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0) + ) + + var lastRowItemCount = component.reactionItems.count % itemsPerRow + if lastRowItemCount == 0 && !component.reactionItems.isEmpty { + lastRowItemCount = itemsPerRow + } + let trailingLineWidth = sideInset + CGFloat(lastRowItemCount) * (itemSize + itemSpacing) + placeholderSpacing + + var contentHeight: CGFloat = verticalInset * 2.0 + CGFloat(rowCount) * itemSize + CGFloat(max(0, rowCount - 1)) * itemSpacing + let trailingPlaceholderFrame: CGRect + if availableSize.width - sideInset - trailingLineWidth < trailingPlaceholderSize.width { + contentHeight += itemSize + itemSpacing + trailingPlaceholderFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalInset + CGFloat(rowCount) * (itemSize + itemSpacing) + floor((itemSize - trailingPlaceholderSize.height) * 0.5)), size: trailingPlaceholderSize) + } else { + trailingPlaceholderFrame = CGRect(origin: CGPoint(x: trailingLineWidth, y: verticalInset + CGFloat(rowCount - 1) * (itemSize + itemSpacing) + floor((itemSize - trailingPlaceholderSize.height) * 0.5)), size: trailingPlaceholderSize) + } + + if let trailingPlaceholderView = self.trailingPlaceholder.view { + if trailingPlaceholderView.superview == nil { + trailingPlaceholderView.layer.anchorPoint = CGPoint() + self.addSubview(trailingPlaceholderView) + self.addSubview(self.caretIndicator) + } + transition.setPosition(view: trailingPlaceholderView, position: trailingPlaceholderFrame.origin) + trailingPlaceholderView.bounds = CGRect(origin: CGPoint(), size: trailingPlaceholderFrame.size) + } + + self.caretIndicator.tintColor = component.theme.list.itemAccentColor + self.caretIndicator.isHidden = !component.isInputActive + + var caretFrame = CGRect(origin: CGPoint(x: trailingPlaceholderFrame.minX, y: trailingPlaceholderFrame.minY + floorToScreenPixels((trailingPlaceholderFrame.height - 22.0) * 0.5)), size: CGSize(width: 2.0, height: 22.0)) + + var validIds: [Int64] = [] + for i in 0 ..< component.reactionItems.count { + let item = component.reactionItems[i] + let itemKey = item.file.fileId.id + validIds.append(itemKey) + + let itemFrame = CGRect(origin: CGPoint(x: sideInset + CGFloat(i % itemsPerRow) * (itemSize + itemSpacing), y: verticalInset + CGFloat(i / itemsPerRow) * (itemSize + itemSpacing)), size: CGSize(width: itemSize, height: itemSize)) + + var itemTransition = transition + var animateIn = false + let itemLayer: EmojiPagerContentComponent.View.ItemLayer + if let current = self.itemLayers[itemKey] { + itemLayer = current + } else { + itemTransition = .immediate + animateIn = true + + let animationData = EntityKeyboardAnimationData( + file: item.file + ) + itemLayer = EmojiPagerContentComponent.View.ItemLayer( + item: EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: item.file, + subgroupId: nil, + icon: .none, + tintMode: item.file.isCustomTemplateEmoji ? .primary : .none + ), + context: component.context, + attemptSynchronousLoad: false, + content: EmojiPagerContentComponent.ItemContent.animation(animationData), + cache: component.context.animationCache, + renderer: component.context.animationRenderer, + placeholderColor: component.theme.list.mediaPlaceholderColor, + blurredBadgeColor: .clear, + accentIconColor: component.theme.list.itemPrimaryTextColor, + pointSize: CGSize(width: 32.0, height: 32.0), + onUpdateDisplayPlaceholder: { _, _ in + } + ) + self.itemLayers[itemKey] = itemLayer + self.layer.addSublayer(itemLayer) + } + itemLayer.isVisibleForAnimations = true + + switch itemLayer.item.tintMode { + case .none: + itemLayer.layerTintColor = nil + case .accent, .primary, .custom: + itemLayer.layerTintColor = component.theme.list.itemPrimaryTextColor.cgColor + } + + itemTransition.setFrame(layer: itemLayer, frame: itemFrame) + + if component.caretPosition == i { + caretFrame = CGRect(origin: CGPoint(x: itemFrame.minX - 2.0, y: itemFrame.minY + floorToScreenPixels((itemFrame.height - 22.0) * 0.5)), size: CGSize(width: 2.0, height: 22.0)) + } else if i == component.reactionItems.count - 1 && component.caretPosition == i + 1 { + caretFrame = CGRect(origin: CGPoint(x: itemFrame.maxX + itemSpacing, y: itemFrame.minY + floorToScreenPixels((itemFrame.height - 22.0) * 0.5)), size: CGSize(width: 2.0, height: 22.0)) + } + + if animateIn, !transition.animation.isImmediate { + itemLayer.animateScale(from: 0.001, to: 1.0, duration: 0.2) + itemLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + } + + var removedIds: [Int64] = [] + for (key, itemLayer) in self.itemLayers { + if !validIds.contains(key) { + removedIds.append(key) + + if !transition.animation.isImmediate { + itemLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak itemLayer] _ in + itemLayer?.removeFromSuperlayer() + }) + itemLayer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false) + } else { + itemLayer.removeFromSuperlayer() + } + } + } + for key in removedIds { + self.itemLayers.removeValue(forKey: key) + } + + if !transition.animation.isImmediate && abs(caretFrame.midY - self.caretIndicator.center.y) > 2.0 { + if let caretSnapshot = self.caretIndicator.snapshotView(afterScreenUpdates: false) { + caretSnapshot.frame = self.caretIndicator.frame + self.insertSubview(caretSnapshot, aboveSubview: self.caretIndicator) + caretSnapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak caretSnapshot] _ in + caretSnapshot?.removeFromSuperview() + }) + caretSnapshot.layer.animateScale(from: 1.0, to: 0.001, duration: 0.15, removeOnCompletion: false) + } + self.caretIndicator.frame = caretFrame + self.caretIndicator.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) + self.caretIndicator.layer.animateScale(from: 0.001, to: 1.0, duration: 0.15) + } else { + transition.setFrame(view: self.caretIndicator, frame: caretFrame) + } + + return CGSize(width: availableSize.width, height: contentHeight) + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/EmojiSelectionComponent.swift b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/EmojiSelectionComponent.swift new file mode 100644 index 00000000000..44f8bb7dbee --- /dev/null +++ b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/EmojiSelectionComponent.swift @@ -0,0 +1,296 @@ +import Foundation +import UIKit +import Display +import TelegramPresentationData +import ComponentFlow +import ComponentDisplayAdapters +import EntityKeyboard +import AccountContext +import PagerComponent +import AudioToolbox + +public final class EmojiSelectionComponent: Component { + public typealias EnvironmentType = Empty + + public let theme: PresentationTheme + public let strings: PresentationStrings + public let sideInset: CGFloat + public let bottomInset: CGFloat + public let deviceMetrics: DeviceMetrics + public let emojiContent: EmojiPagerContentComponent + public let backgroundIconColor: UIColor? + public let backgroundColor: UIColor + public let separatorColor: UIColor + public let backspace: (() -> Void)? + + public init( + theme: PresentationTheme, + strings: PresentationStrings, + sideInset: CGFloat, + bottomInset: CGFloat, + deviceMetrics: DeviceMetrics, + emojiContent: EmojiPagerContentComponent, + backgroundIconColor: UIColor?, + backgroundColor: UIColor, + separatorColor: UIColor, + backspace: (() -> Void)? + ) { + self.theme = theme + self.strings = strings + self.sideInset = sideInset + self.bottomInset = bottomInset + self.deviceMetrics = deviceMetrics + self.emojiContent = emojiContent + self.backgroundIconColor = backgroundIconColor + self.backgroundColor = backgroundColor + self.separatorColor = separatorColor + self.backspace = backspace + } + + public static func ==(lhs: EmojiSelectionComponent, rhs: EmojiSelectionComponent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings != rhs.strings { + return false + } + if lhs.sideInset != rhs.sideInset { + return false + } + if lhs.bottomInset != rhs.bottomInset { + return false + } + if lhs.deviceMetrics != rhs.deviceMetrics { + return false + } + if lhs.emojiContent != rhs.emojiContent { + return false + } + if lhs.backgroundIconColor != rhs.backgroundIconColor { + return false + } + if lhs.backgroundColor != rhs.backgroundColor { + return false + } + if lhs.separatorColor != rhs.separatorColor { + return false + } + if (lhs.backspace == nil) != (rhs.backspace == nil) { + return false + } + return true + } + + public final class View: UIView { + private let keyboardView: ComponentView + private let keyboardClippingView: UIView + private let panelHostView: PagerExternalTopPanelContainer + private let panelBackgroundView: BlurredBackgroundView + private let panelSeparatorView: UIView + private let shadowView: UIImageView + private let cornersView: UIImageView + + private let backspaceButton = ComponentView() + private let backspaceBackgroundView: UIImageView + + private var component: EmojiSelectionComponent? + private weak var state: EmptyComponentState? + + override init(frame: CGRect) { + self.keyboardView = ComponentView() + self.keyboardClippingView = UIView() + self.panelHostView = PagerExternalTopPanelContainer() + self.panelBackgroundView = BlurredBackgroundView(color: .clear, enableBlur: true) + self.panelSeparatorView = UIView() + self.shadowView = UIImageView() + self.cornersView = UIImageView() + + self.backspaceBackgroundView = UIImageView() + + super.init(frame: frame) + + self.addSubview(self.keyboardClippingView) + self.addSubview(self.panelBackgroundView) + self.addSubview(self.panelSeparatorView) + self.addSubview(self.panelHostView) + self.addSubview(self.cornersView) + self.addSubview(self.shadowView) + + self.shadowView.image = generateImage(CGSize(width: 16.0, height: 16.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setShadow(offset: CGSize(), blur: 40.0, color: UIColor(white: 0.0, alpha: 0.05).cgColor) + context.setFillColor(UIColor.black.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: 8.0), size: size)) + context.setBlendMode(.copy) + context.setFillColor(UIColor.clear.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: 8.0), size: size).insetBy(dx: -0.5, dy: -0.5)) + })?.stretchableImage(withLeftCapWidth: 8, topCapHeight: 16) + + self.cornersView.image = generateImage(CGSize(width: 16.0 + 1.0, height: 16.0), rotatedContext: { size, context in + context.setFillColor(UIColor.white.cgColor) + context.fill(CGRect(origin: CGPoint(), size: size)) + context.setBlendMode(.copy) + context.setFillColor(UIColor.clear.cgColor) + context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: 0.0, y: 8.0), size: size), cornerRadius: 8.0).cgPath) + context.fillPath() + context.clear(CGRect(origin: CGPoint(x: 8.0, y: 0.0), size: CGSize(width: 1.0, height: size.height))) + })?.withRenderingMode(.alwaysTemplate).stretchableImage(withLeftCapWidth: 8, topCapHeight: 16) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + func update(component: EmojiSelectionComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + self.backgroundColor = component.backgroundColor + let panelBackgroundColor = component.backgroundColor.withMultipliedAlpha(0.85) + self.panelBackgroundView.updateColor(color: panelBackgroundColor, transition: .immediate) + self.panelSeparatorView.backgroundColor = component.separatorColor + + let previousComponent = self.component + self.component = component + self.state = state + + self.cornersView.tintColor = component.theme.list.blocksBackgroundColor + transition.setFrame(view: self.cornersView, frame: CGRect(origin: CGPoint(x: 0.0, y: -8.0), size: CGSize(width: availableSize.width, height: 16.0))) + + transition.setFrame(view: self.shadowView, frame: CGRect(origin: CGPoint(x: 0.0, y: -8.0), size: CGSize(width: availableSize.width, height: 16.0))) + + let topPanelHeight: CGFloat = 42.0 + + let backspaceButtonInset = UIEdgeInsets(top: 9.0, left: 0.0, bottom: 36.0, right: 9.0) + let backspaceButtonSize = CGSize(width: 36.0, height: 36.0) + + let _ = self.backspaceButton.update( + transition: transition, + component: AnyComponent(Button( + content: AnyComponent(HStack([], spacing: 0.0)), + action: { [weak self] in + guard let self, let component = self.component else { + return + } + component.backspace?() + AudioServicesPlaySystemSound(1155) + } + ).withHoldAction({ [weak self] in + guard let self, let component = self.component else { + return + } + AudioServicesPlaySystemSound(1155) + component.backspace?() + })), + environment: {}, + containerSize: backspaceButtonSize + ) + + if previousComponent?.theme !== component.theme { + self.backspaceBackgroundView.image = generateImage(CGSize(width: backspaceButtonSize.width + 12.0 * 2.0, height: backspaceButtonSize.height + 12.0 * 2.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setShadow(offset: CGSize(), blur: 40.0, color: UIColor(white: 0.0, alpha: 0.15).cgColor) + context.setFillColor(component.theme.list.plainBackgroundColor.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(x: 12.0, y: 12.0), size: backspaceButtonSize)) + + context.setShadow(offset: CGSize(), blur: 0.0, color: nil) + if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/EntityInputClearIcon"), color: component.theme.chat.inputMediaPanel.panelIconColor) { + let imageSize = image.size + context.draw(image.cgImage!, in: CGRect(origin: CGPoint(x: 12.0 + floor((backspaceButtonSize.width - imageSize.width) * 0.5) - 1.0, y: 12.0 + floor((backspaceButtonSize.height - imageSize.height) * 0.5)), size: imageSize)) + } + }) + } + self.backspaceBackgroundView.frame = CGRect(origin: CGPoint(), size: backspaceButtonSize).insetBy(dx: -12.0, dy: -12.0) + let backspaceButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - component.sideInset - backspaceButtonInset.right - backspaceButtonSize.width, y: availableSize.height - component.bottomInset - backspaceButtonInset.bottom), size: backspaceButtonSize) + + if let backspaceButtonView = self.backspaceButton.view { + if backspaceButtonView.superview == nil { + backspaceButtonView.addSubview(self.backspaceBackgroundView) + self.addSubview(backspaceButtonView) + } + transition.setPosition(view: backspaceButtonView, position: backspaceButtonFrame.center) + transition.setBounds(view: backspaceButtonView, bounds: CGRect(origin: CGPoint(), size: backspaceButtonFrame.size)) + + if component.backspace != nil { + transition.setAlpha(view: backspaceButtonView, alpha: 1.0) + transition.setScale(view: backspaceButtonView, scale: 1.0) + } else { + transition.setAlpha(view: backspaceButtonView, alpha: 0.0) + transition.setScale(view: backspaceButtonView, scale: 0.001) + } + } + + let keyboardSize = self.keyboardView.update( + transition: transition.withUserData(EmojiPagerContentComponent.SynchronousLoadBehavior(isDisabled: true)), + component: AnyComponent(EntityKeyboardComponent( + theme: component.theme, + strings: component.strings, + isContentInFocus: true, + containerInsets: UIEdgeInsets(top: topPanelHeight - 34.0, left: component.sideInset, bottom: component.bottomInset + 16.0, right: component.sideInset), + topPanelInsets: UIEdgeInsets(top: 0.0, left: 4.0, bottom: 0.0, right: 4.0), + emojiContent: component.emojiContent.withCustomTintColor(component.theme.list.itemPrimaryTextColor), + stickerContent: nil, + maskContent: nil, + gifContent: nil, + hasRecentGifs: false, + availableGifSearchEmojies: [], + defaultToEmojiTab: true, + externalTopPanelContainer: self.panelHostView, + externalBottomPanelContainer: nil, + displayTopPanelBackground: .blur, + topPanelExtensionUpdated: { _, _ in }, + topPanelScrollingOffset: { _, _ in }, + hideInputUpdated: { _, _, _ in }, + hideTopPanelUpdated: { _, _ in }, + switchToTextInput: {}, + switchToGifSubject: { _ in }, + reorderItems: { _, _ in }, + makeSearchContainerNode: { _ in return nil }, + contentIdUpdated: { _ in }, + deviceMetrics: component.deviceMetrics, + hiddenInputHeight: 0.0, + inputHeight: 0.0, + displayBottomPanel: false, + isExpanded: true, + clipContentToTopPanel: false, + useExternalSearchContainer: false, + customTintColor: component.backgroundIconColor + )), + environment: {}, + containerSize: availableSize + ) + if let keyboardComponentView = self.keyboardView.view { + if keyboardComponentView.superview == nil { + self.keyboardClippingView.addSubview(keyboardComponentView) + } + + if panelBackgroundColor.alpha < 0.01 { + self.keyboardClippingView.clipsToBounds = true + } else { + self.keyboardClippingView.clipsToBounds = false + } + + transition.setFrame(view: self.keyboardClippingView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight), size: CGSize(width: availableSize.width, height: availableSize.height - topPanelHeight))) + + transition.setFrame(view: keyboardComponentView, frame: CGRect(origin: CGPoint(x: 0.0, y: -topPanelHeight), size: keyboardSize)) + transition.setFrame(view: self.panelHostView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight - 34.0), size: CGSize(width: keyboardSize.width, height: 0.0))) + + transition.setFrame(view: self.panelBackgroundView, frame: CGRect(origin: CGPoint(), size: CGSize(width: keyboardSize.width, height: topPanelHeight))) + self.panelBackgroundView.update(size: self.panelBackgroundView.bounds.size, transition: transition.containedViewLayoutTransition) + + transition.setFrame(view: self.panelSeparatorView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight), size: CGSize(width: keyboardSize.width, height: UIScreenPixel))) + transition.setAlpha(view: self.panelSeparatorView, alpha: 1.0) + } + + return availableSize + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/ListSwitchItemComponent.swift b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/ListSwitchItemComponent.swift new file mode 100644 index 00000000000..5bc2a486423 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/ListSwitchItemComponent.swift @@ -0,0 +1,116 @@ +import Foundation +import UIKit +import Display +import TelegramPresentationData +import ComponentFlow +import ComponentDisplayAdapters +import SwitchComponent + +final class ListSwitchItemComponent: Component { + let theme: PresentationTheme + let title: String + let value: Bool + let valueUpdated: (Bool) -> Void + + init( + theme: PresentationTheme, + title: String, + value: Bool, + valueUpdated: @escaping (Bool) -> Void + ) { + self.theme = theme + self.title = title + self.value = value + self.valueUpdated = valueUpdated + } + + static func ==(lhs: ListSwitchItemComponent, rhs: ListSwitchItemComponent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.title != rhs.title { + return false + } + if lhs.value != rhs.value { + return false + } + return true + } + + final class View: UIView { + private let title = ComponentView() + private let switchView = ComponentView() + + private var component: ListSwitchItemComponent? + private weak var state: EmptyComponentState? + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init(coder: NSCoder) { + preconditionFailure() + } + + func update(component: ListSwitchItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + self.component = component + self.state = state + + self.backgroundColor = component.theme.list.itemBlocksBackgroundColor + self.layer.cornerRadius = 12.0 + + let size = CGSize(width: availableSize.width, height: 44.0) + let rightInset: CGFloat = 16.0 + let leftInset: CGFloat = 16.0 + let spacing: CGFloat = 8.0 + + let switchSize = self.switchView.update( + transition: transition, + component: AnyComponent(SwitchComponent( + value: component.value, + valueUpdated: { [weak self] value in + guard let self else { + return + } + self.component?.valueUpdated(value) + } + )), + environment: {}, + containerSize: size + ) + let switchFrame = CGRect(origin: CGPoint(x: size.width - rightInset - switchSize.width, y: floor((size.height - switchSize.height) * 0.5)), size: switchSize) + if let switchComponentView = self.switchView.view { + if switchComponentView.superview == nil { + self.addSubview(switchComponentView) + } + transition.setFrame(view: switchComponentView, frame: switchFrame) + } + + let titleSize = self.title.update( + transition: .immediate, + component: AnyComponent(Text(text: component.title, font: Font.regular(17.0), color: component.theme.list.itemPrimaryTextColor)), + environment: {}, + containerSize: CGSize(width: max(1.0, switchFrame.minX - spacing - leftInset), height: .greatestFiniteMagnitude) + ) + let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((size.height - titleSize.height) * 0.5)), size: titleSize) + if let titleView = self.title.view { + if titleView.superview == nil { + titleView.layer.anchorPoint = CGPoint() + self.addSubview(titleView) + } + transition.setPosition(view: titleView, position: titleFrame.origin) + titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size) + } + + return size + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift new file mode 100644 index 00000000000..08dfdab46a6 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift @@ -0,0 +1,1228 @@ +import Foundation +import UIKit +import Display +import TelegramPresentationData +import ComponentFlow +import ComponentDisplayAdapters +import AppBundle +import ViewControllerComponent +import AccountContext +import TelegramCore +import Postbox +import SwiftSignalKit +import EntityKeyboard +import MultilineTextComponent +import Markdown +import ButtonComponent +import PremiumUI +import UndoUI +import BundleIconComponent +import AnimatedTextComponent +import TextFormat +import AudioToolbox + +private final class ButtonSubtitleComponent: CombinedComponent { + let count: Int + let theme: PresentationTheme + let strings: PresentationStrings + + init(count: Int, theme: PresentationTheme, strings: PresentationStrings) { + self.count = count + self.theme = theme + self.strings = strings + } + + static func ==(lhs: ButtonSubtitleComponent, rhs: ButtonSubtitleComponent) -> Bool { + if lhs.count != rhs.count { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + return true + } + + static var body: Body { + let icon = Child(BundleIconComponent.self) + let text = Child(AnimatedTextComponent.self) + + return { context in + let icon = icon.update( + component: BundleIconComponent( + name: "Chat/Input/Accessory Panels/TextLockIcon", + tintColor: context.component.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.7), + maxSize: CGSize(width: 10.0, height: 10.0) + ), + availableSize: CGSize(width: 100.0, height: 100.0), + transition: context.transition + ) + var textItems: [AnimatedTextComponent.Item] = [] + + let levelString = context.component.strings.ChannelReactions_LevelRequiredLabel("") + var previousIndex = 0 + let nsLevelString = levelString.string as NSString + for range in levelString.ranges.sorted(by: { $0.range.lowerBound < $1.range.lowerBound }) { + if range.range.lowerBound > previousIndex { + textItems.append(AnimatedTextComponent.Item(id: AnyHashable(range.index), content: .text(nsLevelString.substring(with: NSRange(location: previousIndex, length: range.range.lowerBound - previousIndex))))) + } + if range.index == 0 { + textItems.append(AnimatedTextComponent.Item(id: AnyHashable(range.index), content: .number(context.component.count, minDigits: 1))) + } + previousIndex = range.range.upperBound + } + if nsLevelString.length > previousIndex { + textItems.append(AnimatedTextComponent.Item(id: AnyHashable(100), content: .text(nsLevelString.substring(with: NSRange(location: previousIndex, length: nsLevelString.length - previousIndex))))) + } + + let text = text.update( + component: AnimatedTextComponent(font: Font.medium(11.0), color: context.component.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.7), items: textItems), + availableSize: CGSize(width: context.availableSize.width - 20.0, height: 100.0), + transition: context.transition + ) + + let spacing: CGFloat = 3.0 + let size = CGSize(width: icon.size.width + spacing + text.size.width, height: text.size.height) + context.add(icon + .position(icon.size.centered(in: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: icon.size.width, height: size.height))).center) + ) + context.add(text + .position(text.size.centered(in: CGRect(origin: CGPoint(x: icon.size.width + spacing, y: 0.0), size: text.size)).center) + ) + + return size + } + } +} + +final class PeerAllowedReactionsScreenComponent: Component { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + let context: AccountContext + let peerId: EnginePeer.Id + let initialContent: PeerAllowedReactionsScreen.Content + + init( + context: AccountContext, + peerId: EnginePeer.Id, + initialContent: PeerAllowedReactionsScreen.Content + ) { + self.context = context + self.peerId = peerId + self.initialContent = initialContent + } + + static func ==(lhs: PeerAllowedReactionsScreenComponent, rhs: PeerAllowedReactionsScreenComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.peerId != rhs.peerId { + return false + } + + return true + } + + final class View: UIView, UIScrollViewDelegate { + private let scrollView: UIScrollView + private let switchItem = ComponentView() + private let switchInfoText = ComponentView() + private var reactionsTitleText: ComponentView? + private var reactionsInfoText: ComponentView? + private var reactionInput: ComponentView? + private let actionButton = ComponentView() + + private var reactionSelectionControl: ComponentView? + + private var isUpdating: Bool = false + + private var component: PeerAllowedReactionsScreenComponent? + private(set) weak var state: EmptyComponentState? + private var environment: EnvironmentType? + + private var boostStatus: ChannelBoostStatus? + private var boostStatusDisposable: Disposable? + + private var isEnabled: Bool = false + private var availableReactions: AvailableReactions? + private var enabledReactions: [EmojiComponentReactionItem]? + private var appliedAllowedReactions: PeerAllowedReactions? + + private var emojiContent: EmojiPagerContentComponent? + private var emojiContentDisposable: Disposable? + private var caretPosition: Int? + + private var displayInput: Bool = false + private var recenterOnCaret: Bool = false + + private var isApplyingSettings: Bool = false + private var applyDisposable: Disposable? + + private var resolveStickersBotDisposable: Disposable? + + private weak var currentUndoController: UndoOverlayController? + + override init(frame: CGRect) { + self.scrollView = UIScrollView() + self.scrollView.showsVerticalScrollIndicator = true + self.scrollView.showsHorizontalScrollIndicator = false + self.scrollView.scrollsToTop = false + self.scrollView.delaysContentTouches = false + self.scrollView.canCancelContentTouches = true + self.scrollView.contentInsetAdjustmentBehavior = .never + self.scrollView.alwaysBounceVertical = true + + super.init(frame: frame) + + self.scrollView.delegate = self + self.addSubview(self.scrollView) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.emojiContentDisposable?.dispose() + self.applyDisposable?.dispose() + self.boostStatusDisposable?.dispose() + self.resolveStickersBotDisposable?.dispose() + } + + func scrollToTop() { + self.scrollView.setContentOffset(CGPoint(), animated: true) + } + + func attemptNavigation(complete: @escaping () -> Void) -> Bool { + guard let component = self.component else { + return true + } + if self.isApplyingSettings { + return true + } + guard var enabledReactions = self.enabledReactions else { + return true + } + if !self.isEnabled { + enabledReactions.removeAll() + } + guard let availableReactions = self.availableReactions else { + return true + } + + let allowedReactions: PeerAllowedReactions + if self.isEnabled { + if Set(availableReactions.reactions.filter({ $0.isEnabled }).map(\.value)) == Set(enabledReactions.map(\.reaction)) { + allowedReactions = .all + } else { + allowedReactions = .limited(enabledReactions.map(\.reaction)) + } + } else { + allowedReactions = .empty + } + + if self.appliedAllowedReactions != allowedReactions { + if case .empty = allowedReactions { + self.applySettings(standalone: true) + } else { + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.ChannelReactions_UnsavedChangesAlertTitle, text: presentationData.strings.ChannelReactions_UnsavedChangesAlertText, actions: [ + TextAlertAction(type: .genericAction, title: presentationData.strings.ChannelReactions_UnsavedChangesAlertDiscard, action: { [weak self] in + guard let self else { + return + } + self.environment?.controller()?.dismiss() + }), + TextAlertAction(type: .defaultAction, title: presentationData.strings.ChannelReactions_UnsavedChangesAlertApply, action: { [weak self] in + guard let self else { + return + } + self.applySettings(standalone: false) + }) + ]), in: .window(.root)) + + return false + } + } + + return true + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + self.updateScrolling(transition: .immediate) + } + + private func updateScrolling(transition: Transition) { + let navigationAlphaDistance: CGFloat = 16.0 + let navigationAlpha: CGFloat = max(0.0, min(1.0, self.scrollView.contentOffset.y / navigationAlphaDistance)) + if let controller = self.environment?.controller(), let navigationBar = controller.navigationBar { + transition.setAlpha(layer: navigationBar.backgroundNode.layer, alpha: navigationAlpha) + transition.setAlpha(layer: navigationBar.stripeNode.layer, alpha: navigationAlpha) + } + } + + private func applySettings(standalone: Bool) { + guard let component = self.component else { + return + } + if self.isApplyingSettings { + return + } + guard var enabledReactions = self.enabledReactions else { + return + } + if !self.isEnabled { + enabledReactions.removeAll() + } + + guard let availableReactions = self.availableReactions else { + return + } + + let customReactions = enabledReactions.filter({ item in + switch item.reaction { + case .custom: + return true + case .builtin: + return false + } + }) + + if let boostStatus = self.boostStatus, !customReactions.isEmpty, customReactions.count > boostStatus.level { + self.displayPremiumScreen(reactionCount: customReactions.count) + return + } + + self.isApplyingSettings = true + self.state?.updated(transition: .immediate) + + self.applyDisposable?.dispose() + + let allowedReactions: PeerAllowedReactions + if self.isEnabled { + if Set(availableReactions.reactions.filter({ $0.isEnabled }).map(\.value)) == Set(enabledReactions.map(\.reaction)) { + allowedReactions = .all + } else if enabledReactions.isEmpty { + allowedReactions = .empty + } else { + allowedReactions = .limited(enabledReactions.map(\.reaction)) + } + } else { + allowedReactions = .empty + } + let applyDisposable = (component.context.engine.peers.updatePeerAllowedReactions(peerId: component.peerId, allowedReactions: allowedReactions) + |> deliverOnMainQueue).start(error: { [weak self] error in + guard let self, let component = self.component else { + return + } + self.isApplyingSettings = false + self.state?.updated(transition: .immediate) + + if !standalone { + switch error { + case .boostRequired: + self.displayPremiumScreen(reactionCount: customReactions.count) + case .generic: + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + } + } + }, completed: { [weak self] in + guard let self else { + return + } + self.appliedAllowedReactions = allowedReactions + if !standalone { + self.environment?.controller()?.dismiss() + } + }) + + if standalone { + let _ = applyDisposable + } else { + self.applyDisposable = applyDisposable + } + } + + private func displayPremiumScreen(reactionCount: Int) { + guard let component = self.component else { + return + } + + let _ = (component.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: component.peerId)) + |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in + guard let self, let component = self.component, let peer, let status = self.boostStatus else { + return + } + + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 }) + + let link = status.url + let controller = PremiumLimitScreen(context: component.context, subject: .storiesChannelBoost(peer: peer, boostSubject: .channelReactions(reactionCount: reactionCount), isCurrent: true, level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), link: link, myBoostCount: 0, canBoostAgain: false), count: Int32(status.boosts), action: { [weak self] in + guard let self, let component = self.component else { + return true + } + + UIPasteboard.general.string = link + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + self.environment?.controller()?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.ChannelBoost_BoostLinkCopied), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in return false }), in: .current) + return true + }, openStats: { [weak self] in + guard let self else { + return + } + self.openBoostStats() + }, openGift: premiumConfiguration.giveawayGiftsPurchaseAvailable ? { [weak self] in + guard let self, let component = self.component else { + return + } + let controller = createGiveawayController(context: component.context, peerId: component.peerId, subject: .generic) + self.environment?.controller()?.push(controller) + } : nil) + self.environment?.controller()?.push(controller) + + HapticFeedback().impact(.light) + }) + } + + private func openBoostStats() { + guard let component = self.component, let boostStatus = self.boostStatus else { + return + } + let statsController = component.context.sharedContext.makeChannelStatsController(context: component.context, updatedPresentationData: nil, peerId: component.peerId, boosts: true, boostStatus: boostStatus) + self.environment?.controller()?.push(statsController) + } + + func update(component: PeerAllowedReactionsScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } + + + let environment = environment[EnvironmentType.self].value + let themeUpdated = self.environment?.theme !== environment.theme + self.environment = environment + + self.component = component + self.state = state + + let topInset: CGFloat = 24.0 + let bottomInset: CGFloat = 8.0 + let sideInset: CGFloat = 16.0 + environment.safeInsets.left + let textSideInset: CGFloat = 16.0 + + let enabledReactions: [EmojiComponentReactionItem] + if let current = self.enabledReactions { + enabledReactions = current + } else { + enabledReactions = component.initialContent.enabledReactions + self.enabledReactions = enabledReactions + self.availableReactions = component.initialContent.availableReactions + self.isEnabled = component.initialContent.isEnabled + self.appliedAllowedReactions = component.initialContent.allowedReactions + } + var caretPosition = self.caretPosition ?? enabledReactions.count + caretPosition = max(0, min(enabledReactions.count, caretPosition)) + self.caretPosition = caretPosition + + if self.emojiContentDisposable == nil { + let emojiContent = EmojiPagerContentComponent.emojiInputData( + context: component.context, + animationCache: component.context.animationCache, + animationRenderer: component.context.animationRenderer, + isStandalone: false, + subject: .reactionList, + hasTrending: false, + topReactionItems: [], + areUnicodeEmojiEnabled: false, + areCustomEmojiEnabled: true, + chatPeerId: nil, + selectedItems: Set(), + backgroundIconColor: nil, + hasSearch: false, + forceHasPremium: true + ) + self.emojiContentDisposable = (emojiContent + |> deliverOnMainQueue).start(next: { [weak self] emojiContent in + guard let self else { + return + } + self.emojiContent = emojiContent + + emojiContent.inputInteractionHolder.inputInteraction = EmojiPagerContentComponent.InputInteraction( + performItemAction: { [weak self] _, item, _, _, _, _ in + guard let self, var enabledReactions = self.enabledReactions else { + return + } + if self.isApplyingSettings { + return + } + guard let itemFile = item.itemFile else { + return + } + + AudioServicesPlaySystemSound(0x450) + + if let index = enabledReactions.firstIndex(where: { $0.file.fileId.id == itemFile.fileId.id }) { + enabledReactions.remove(at: index) + if let caretPosition = self.caretPosition, caretPosition > index { + self.caretPosition = max(0, caretPosition - 1) + } + } else { + if enabledReactions.count >= 100 { + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + + var animateAsReplacement = false + if let currentUndoController = self.currentUndoController { + currentUndoController.dismiss() + animateAsReplacement = true + } + + let undoController = UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.ChannelReactions_ToastMaxReactionsReached, timeout: nil, customUndoText: nil), elevatedLayout: false, position: .bottom, animateInAsReplacement: animateAsReplacement, action: { _ in return false }) + self.currentUndoController = undoController + self.environment?.controller()?.present(undoController, in: .current) + return + } + + let reaction: MessageReaction.Reaction + if let availableReactions = self.availableReactions, let reactionItem = availableReactions.reactions.filter({ $0.isEnabled }).first(where: { $0.selectAnimation.fileId.id == itemFile.fileId.id }) { + reaction = reactionItem.value + } else { + reaction = .custom(itemFile.fileId.id) + + if let boostStatus = self.boostStatus { + let enabledCustomReactions = enabledReactions.filter({ item in + switch item.reaction { + case .custom: + return true + case .builtin: + return false + } + }) + + let nextCustomReactionCount = enabledCustomReactions.count + 1 + if nextCustomReactionCount > boostStatus.level { + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + + var animateAsReplacement = false + if let currentUndoController = self.currentUndoController { + currentUndoController.dismiss() + animateAsReplacement = true + } + + let undoController = UndoOverlayController(presentationData: presentationData, content: .customEmoji(context: component.context, file: itemFile, loop: false, title: nil, text: presentationData.strings.ChannelReactions_ToastLevelBoostRequired("\(nextCustomReactionCount)", "\(nextCustomReactionCount)").string, undoText: nil, customAction: nil), elevatedLayout: false, position: .bottom, animateInAsReplacement: animateAsReplacement, action: { _ in return false }) + self.currentUndoController = undoController + self.environment?.controller()?.present(undoController, in: .current) + } + } + } + let item = EmojiComponentReactionItem(reaction: reaction, file: itemFile) + + if let caretPosition = self.caretPosition, caretPosition < enabledReactions.count { + enabledReactions.insert(item, at: caretPosition) + self.caretPosition = caretPosition + 1 + } else { + enabledReactions.append(item) + self.caretPosition = enabledReactions.count + } + self.recenterOnCaret = true + } + self.enabledReactions = enabledReactions + if !self.isUpdating { + self.state?.updated(transition: .spring(duration: 0.25)) + } + }, + deleteBackwards: { + }, + openStickerSettings: { + }, + openFeatured: { + }, + openSearch: { + }, + addGroupAction: { _, _, _ in + }, + clearGroup: { _ in + }, + pushController: { c in + }, + presentController: { c in + }, + presentGlobalOverlayController: { c in + }, + navigationController: { + return nil + }, + requestUpdate: { _ in + }, + updateSearchQuery: { _ in + }, + updateScrollingToItemGroup: { + }, + onScroll: {}, + chatPeerId: nil, + peekBehavior: nil, + customLayout: nil, + externalBackground: nil, + externalExpansionView: nil, + customContentView: nil, + useOpaqueTheme: true, + hideBackground: false, + stateContext: nil, + addImage: nil + ) + + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } + }) + } + + if self.boostStatusDisposable == nil { + self.boostStatusDisposable = (component.context.engine.peers.getChannelBoostStatus(peerId: component.peerId) + |> deliverOnMainQueue).start(next: { [weak self] boostStatus in + guard let self else { + return + } + self.boostStatus = boostStatus + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } + }) + } + + if themeUpdated { + self.backgroundColor = environment.theme.list.blocksBackgroundColor + } + + var contentHeight: CGFloat = 0.0 + contentHeight += environment.navigationHeight + contentHeight += topInset + + let switchSize = self.switchItem.update( + transition: transition, + component: AnyComponent(ListSwitchItemComponent( + theme: environment.theme, + title: environment.strings.PeerInfo_AllowedReactions_AllowAllText, + value: self.isEnabled, + valueUpdated: { [weak self] value in + guard let self else { + return + } + if self.isEnabled != value { + self.isEnabled = value + + if self.isEnabled { + if var enabledReactions = self.enabledReactions, enabledReactions.isEmpty { + if let availableReactions = self.availableReactions { + for reactionItem in availableReactions.reactions.filter({ $0.isEnabled }) { + enabledReactions.append(EmojiComponentReactionItem(reaction: reactionItem.value, file: reactionItem.selectAnimation)) + } + } + self.enabledReactions = enabledReactions + self.caretPosition = enabledReactions.count + } + } else { + self.displayInput = false + } + + self.state?.updated(transition: .easeInOut(duration: 0.25)) + } + } + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: .greatestFiniteMagnitude) + ) + let switchFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: switchSize) + if let switchView = self.switchItem.view { + if switchView.superview == nil { + self.scrollView.addSubview(switchView) + } + transition.setFrame(view: switchView, frame: switchFrame) + } + contentHeight += switchSize.height + contentHeight += 7.0 + + let switchInfoTextSize = self.switchInfoText.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: environment.strings.ChannelReactions_GeneralInfoLabel, + font: Font.regular(13.0), + textColor: environment.theme.list.freeTextColor + )), + maximumNumberOfLines: 0 + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0 - textSideInset * 2.0, height: .greatestFiniteMagnitude) + ) + let switchInfoTextFrame = CGRect(origin: CGPoint(x: sideInset + textSideInset, y: contentHeight), size: switchInfoTextSize) + if let switchInfoTextView = self.switchInfoText.view { + if switchInfoTextView.superview == nil { + switchInfoTextView.layer.anchorPoint = CGPoint() + self.scrollView.addSubview(switchInfoTextView) + } + transition.setPosition(view: switchInfoTextView, position: switchInfoTextFrame.origin) + switchInfoTextView.bounds = CGRect(origin: CGPoint(), size: switchInfoTextFrame.size) + } + contentHeight += switchInfoTextSize.height + contentHeight += 37.0 + + if self.isEnabled { + var animateIn = false + + let reactionsTitleText: ComponentView + if let current = self.reactionsTitleText { + reactionsTitleText = current + } else { + reactionsTitleText = ComponentView() + self.reactionsTitleText = reactionsTitleText + animateIn = true + } + + let reactionsTitleTextSize = reactionsTitleText.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: environment.strings.ChannelReactions_ReactionsSectionTitle, + font: Font.regular(13.0), + textColor: environment.theme.list.freeTextColor + )), + maximumNumberOfLines: 0 + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0 - textSideInset * 2.0, height: .greatestFiniteMagnitude) + ) + let reactionsTitleTextFrame = CGRect(origin: CGPoint(x: sideInset + textSideInset, y: contentHeight), size: reactionsTitleTextSize) + if let reactionsTitleTextView = reactionsTitleText.view { + if reactionsTitleTextView.superview == nil { + reactionsTitleTextView.layer.anchorPoint = CGPoint() + self.scrollView.addSubview(reactionsTitleTextView) + } + + if animateIn { + reactionsTitleTextView.frame = reactionsTitleTextFrame + if !transition.animation.isImmediate { + reactionsTitleTextView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + } else { + transition.setPosition(view: reactionsTitleTextView, position: reactionsTitleTextFrame.origin) + reactionsTitleTextView.bounds = CGRect(origin: CGPoint(), size: reactionsTitleTextFrame.size) + } + } + contentHeight += reactionsTitleTextSize.height + contentHeight += 6.0 + + let reactionInput: ComponentView + if let current = self.reactionInput { + reactionInput = current + } else { + reactionInput = ComponentView() + self.reactionInput = reactionInput + } + + //TOOD:localize + let reactionInputSize = reactionInput.update( + transition: animateIn ? .immediate : transition, + component: AnyComponent(EmojiListInputComponent( + context: component.context, + theme: environment.theme, + placeholder: "Add Reactions...", + reactionItems: enabledReactions, + isInputActive: self.displayInput, + caretPosition: caretPosition, + activateInput: { [weak self] in + guard let self else { + return + } + if self.emojiContent != nil && !self.displayInput { + self.displayInput = true + self.recenterOnCaret = true + self.state?.updated(transition: .spring(duration: 0.5)) + } + }, + setCaretPosition: { [weak self] value in + guard let self else { + return + } + if self.caretPosition != value { + self.caretPosition = value + self.state?.updated(transition: .immediate) + } + } + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: .greatestFiniteMagnitude) + ) + let reactionInputFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: reactionInputSize) + if let reactionInputView = reactionInput.view { + if reactionInputView.superview == nil { + self.scrollView.addSubview(reactionInputView) + } + if animateIn { + reactionInputView.frame = reactionInputFrame + if !transition.animation.isImmediate { + reactionInputView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + } else { + transition.setFrame(view: reactionInputView, frame: reactionInputFrame) + } + } + contentHeight += reactionInputSize.height + contentHeight += 7.0 + + let reactionsInfoText: ComponentView + if let current = self.reactionsInfoText { + reactionsInfoText = current + } else { + reactionsInfoText = ComponentView() + self.reactionsInfoText = reactionsInfoText + } + + let body = MarkdownAttributeSet(font: UIFont.systemFont(ofSize: 13.0), textColor: environment.theme.list.freeTextColor) + let link = MarkdownAttributeSet(font: UIFont.systemFont(ofSize: 13.0), textColor: environment.theme.list.itemAccentColor, additionalAttributes: [:]) + let attributes = MarkdownAttributes(body: body, bold: body, link: link, linkAttribute: { contents in + return (TelegramTextAttributes.URL, contents) + }) + let reactionsInfoTextSize = reactionsInfoText.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .markdown(text: environment.strings.ChannelReactions_ReactionsInfoLabel, attributes: attributes), + maximumNumberOfLines: 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 + guard let self, let component = self.component, attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] != nil else { + return + } + self.resolveStickersBotDisposable?.dispose() + self.resolveStickersBotDisposable = (component.context.engine.peers.resolvePeerByName(name: "stickers") + |> mapToSignal { result -> Signal in + guard case let .result(result) = result else { + return .complete() + } + return .single(result) + } + |> deliverOnMainQueue).start(next: { [weak self] peer in + guard let self, let component = self.component, let peer else { + return + } + guard let navigationController = self.environment?.controller()?.navigationController as? NavigationController else { + return + } + component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams( + navigationController: navigationController, + context: component.context, + chatLocation: .peer(peer), + keepStack: .always + )) + }) + } + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0 - textSideInset * 2.0, height: .greatestFiniteMagnitude) + ) + let reactionsInfoTextFrame = CGRect(origin: CGPoint(x: sideInset + textSideInset, y: contentHeight), size: reactionsInfoTextSize) + if let reactionsInfoTextView = reactionsInfoText.view { + if reactionsInfoTextView.superview == nil { + reactionsInfoTextView.layer.anchorPoint = CGPoint() + self.scrollView.addSubview(reactionsInfoTextView) + } + if animateIn { + reactionsInfoTextView.frame = reactionsInfoTextFrame + if !transition.animation.isImmediate { + reactionsInfoTextView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + } else { + transition.setPosition(view: reactionsInfoTextView, position: reactionsInfoTextFrame.origin) + reactionsInfoTextView.bounds = CGRect(origin: CGPoint(), size: reactionsInfoTextFrame.size) + } + } + contentHeight += reactionsInfoTextSize.height + contentHeight += 6.0 + } else { + if let reactionsTitleText = self.reactionsTitleText { + self.reactionsTitleText = nil + if let reactionsTitleTextView = reactionsTitleText.view { + if !transition.animation.isImmediate { + reactionsTitleTextView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak reactionsTitleTextView] _ in + reactionsTitleTextView?.removeFromSuperview() + }) + } else { + reactionsTitleTextView.removeFromSuperview() + } + } + } + + if let reactionInput = self.reactionInput { + self.reactionInput = nil + if let reactionInputView = reactionInput.view { + if !transition.animation.isImmediate { + reactionInputView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak reactionInputView] _ in + reactionInputView?.removeFromSuperview() + }) + } else { + reactionInputView.removeFromSuperview() + } + } + } + + if let reactionsInfoText = self.reactionsInfoText { + self.reactionsInfoText = nil + if let reactionsInfoTextView = reactionsInfoText.view { + if !transition.animation.isImmediate { + reactionsInfoTextView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak reactionsInfoTextView] _ in + reactionsInfoTextView?.removeFromSuperview() + }) + } else { + reactionsInfoTextView.removeFromSuperview() + } + } + } + } + + var buttonContents: [AnyComponentWithIdentity] = [] + buttonContents.append(AnyComponentWithIdentity(id: AnyHashable(0 as Int), component: AnyComponent( + Text(text: environment.strings.ChannelReactions_SaveAction, font: Font.semibold(17.0), color: environment.theme.list.itemCheckColors.foregroundColor) + ))) + + let customReactionCount = self.isEnabled ? enabledReactions.filter({ item in + switch item.reaction { + case .custom: + return true + case .builtin: + return false + } + }).count : 0 + + if let boostStatus = self.boostStatus, customReactionCount > boostStatus.level { + buttonContents.append(AnyComponentWithIdentity(id: AnyHashable(1 as Int), component: AnyComponent(ButtonSubtitleComponent( + count: customReactionCount, + theme: environment.theme, + strings: environment.strings + )))) + } + + let buttonSize = self.actionButton.update( + transition: transition, + component: AnyComponent(ButtonComponent( + background: ButtonComponent.Background( + color: environment.theme.list.itemCheckColors.fillColor, + foreground: environment.theme.list.itemCheckColors.foregroundColor, + pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8) + ), + content: AnyComponentWithIdentity(id: AnyHashable(0 as Int), component: AnyComponent( + VStack(buttonContents, spacing: 3.0) + )), + isEnabled: true, + tintWhenDisabled: false, + displaysProgress: self.isApplyingSettings, + action: { [weak self] in + guard let self else { + return + } + self.applySettings(standalone: false) + } + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 50.0) + ) + contentHeight += buttonSize.height + + var inputHeight: CGFloat = 0.0 + if self.displayInput, let emojiContent = self.emojiContent { + let reactionSelectionControl: ComponentView + var animateIn = false + if let current = self.reactionSelectionControl { + reactionSelectionControl = current + } else { + animateIn = true + reactionSelectionControl = ComponentView() + self.reactionSelectionControl = reactionSelectionControl + } + let reactionSelectionControlSize = reactionSelectionControl.update( + transition: animateIn ? .immediate : transition, + component: AnyComponent(EmojiSelectionComponent( + theme: environment.theme, + strings: environment.strings, + sideInset: environment.safeInsets.left, + bottomInset: environment.safeInsets.bottom, + deviceMetrics: environment.deviceMetrics, + emojiContent: emojiContent.withSelectedItems(Set(enabledReactions.map(\.file.fileId))), + backgroundIconColor: nil, + backgroundColor: environment.theme.list.itemBlocksBackgroundColor, + separatorColor: environment.theme.list.itemBlocksSeparatorColor, + backspace: enabledReactions.isEmpty ? nil : { [weak self] in + guard let self, var enabledReactions = self.enabledReactions, !enabledReactions.isEmpty else { + return + } + if let caretPosition = self.caretPosition, caretPosition < enabledReactions.count { + if caretPosition > 0 { + enabledReactions.remove(at: caretPosition - 1) + self.caretPosition = caretPosition - 1 + self.recenterOnCaret = true + } + } else { + enabledReactions.removeLast() + self.caretPosition = enabledReactions.count + self.recenterOnCaret = true + } + self.enabledReactions = enabledReactions + if !self.isUpdating { + self.state?.updated(transition: .spring(duration: 0.25)) + } + } + )), + environment: {}, + containerSize: CGSize(width: availableSize.width, height: min(340.0, max(50.0, availableSize.height - 200.0))) + ) + let reactionSelectionControlFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - reactionSelectionControlSize.height), size: reactionSelectionControlSize) + if let reactionSelectionControlView = reactionSelectionControl.view { + if reactionSelectionControlView.superview == nil { + self.addSubview(reactionSelectionControlView) + } + if animateIn { + reactionSelectionControlView.frame = reactionSelectionControlFrame + transition.animatePosition(view: reactionSelectionControlView, from: CGPoint(x: 0.0, y: reactionSelectionControlFrame.height), to: CGPoint(), additive: true) + } else { + transition.setFrame(view: reactionSelectionControlView, frame: reactionSelectionControlFrame) + } + } + inputHeight = reactionSelectionControlSize.height + } else if let reactionSelectionControl = self.reactionSelectionControl { + self.reactionSelectionControl = nil + if let reactionSelectionControlView = reactionSelectionControl.view { + transition.setPosition(view: reactionSelectionControlView, position: CGPoint(x: reactionSelectionControlView.center.x, y: availableSize.height + reactionSelectionControlView.bounds.height * 0.5), completion: { [weak reactionSelectionControlView] _ in + reactionSelectionControlView?.removeFromSuperview() + }) + } + } + + let buttonY: CGFloat + + if self.displayInput { + contentHeight += bottomInset + 8.0 + contentHeight += inputHeight + + buttonY = availableSize.height - bottomInset - 8.0 - inputHeight - buttonSize.height + } else { + contentHeight += bottomInset + contentHeight += environment.safeInsets.bottom + + buttonY = availableSize.height - bottomInset - environment.safeInsets.bottom - buttonSize.height + } + + let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: buttonY), size: buttonSize) + if let buttonView = self.actionButton.view { + if buttonView.superview == nil { + self.addSubview(buttonView) + } + transition.setFrame(view: buttonView, frame: buttonFrame) + transition.setAlpha(view: buttonView, alpha: self.isEnabled ? 1.0 : 0.0) + } + + let contentSize = CGSize(width: availableSize.width, height: contentHeight) + if self.scrollView.frame != CGRect(origin: CGPoint(), size: availableSize) { + self.scrollView.frame = CGRect(origin: CGPoint(), size: availableSize) + } + if self.scrollView.contentSize != contentSize { + self.scrollView.contentSize = contentSize + } + let scrollInsets = UIEdgeInsets(top: environment.navigationHeight, left: 0.0, bottom: environment.safeInsets.bottom, right: 0.0) + if self.scrollView.scrollIndicatorInsets != scrollInsets { + self.scrollView.scrollIndicatorInsets = scrollInsets + } + + if self.recenterOnCaret { + self.recenterOnCaret = false + + if let reactionInputView = self.reactionInput?.view as? EmojiListInputComponent.View, let localCaretRect = reactionInputView.caretRect() { + let caretRect = reactionInputView.convert(localCaretRect, to: self.scrollView) + var scrollViewBounds = self.scrollView.bounds + let minButtonDistance: CGFloat = 16.0 + if -scrollViewBounds.minY + caretRect.maxY > buttonFrame.minY - minButtonDistance { + scrollViewBounds.origin.y = -(buttonFrame.minY - minButtonDistance - caretRect.maxY) + if scrollViewBounds.origin.y < 0.0 { + scrollViewBounds.origin.y = 0.0 + } + } + if self.scrollView.bounds != scrollViewBounds { + transition.setBounds(view: self.scrollView, bounds: scrollViewBounds) + } + } + } + + self.updateScrolling(transition: transition) + + return availableSize + } + } + + func makeView() -> View { + return View() + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +public class PeerAllowedReactionsScreen: ViewControllerComponentContainer { + public final class Content: Equatable { + public let isEnabled: Bool + public let enabledReactions: [EmojiComponentReactionItem] + public let availableReactions: AvailableReactions? + public let allowedReactions: PeerAllowedReactions? + + init( + isEnabled: Bool, + enabledReactions: [EmojiComponentReactionItem], + availableReactions: AvailableReactions?, + allowedReactions: PeerAllowedReactions? + ) { + self.isEnabled = isEnabled + self.enabledReactions = enabledReactions + self.availableReactions = availableReactions + self.allowedReactions = allowedReactions + } + + public static func ==(lhs: Content, rhs: Content) -> Bool { + if lhs === rhs { + return true + } + if lhs.isEnabled != rhs.isEnabled { + return false + } + if lhs.enabledReactions != rhs.enabledReactions { + return false + } + if lhs.availableReactions != rhs.availableReactions { + return false + } + if lhs.allowedReactions != rhs.allowedReactions { + return false + } + return true + } + } + + private let context: AccountContext + private var isDismissed: Bool = false + + public init( + context: AccountContext, + peerId: EnginePeer.Id, + initialContent: Content + ) { + self.context = context + + super.init(context: context, component: PeerAllowedReactionsScreenComponent( + context: context, + peerId: peerId, + initialContent: initialContent + ), navigationBarAppearance: .default, theme: .default) + + self.title = context.sharedContext.currentPresentationData.with({ $0 }).strings.ChannelReactions_Reactions + + self.scrollToTop = { [weak self] in + guard let self, let componentView = self.node.hostView.componentView as? PeerAllowedReactionsScreenComponent.View else { + return + } + componentView.scrollToTop() + } + + self.attemptNavigation = { [weak self] complete in + guard let self, let componentView = self.node.hostView.componentView as? PeerAllowedReactionsScreenComponent.View else { + return true + } + + return componentView.attemptNavigation(complete: complete) + } + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + @objc private func cancelPressed() { + self.dismiss() + } + + override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + super.containerLayoutUpdated(layout, transition: transition) + } + + public static func content(context: AccountContext, peerId: EnginePeer.Id) -> Signal { + return combineLatest( + context.engine.stickers.availableReactions(), + context.account.postbox.combinedView(keys: [.cachedPeerData(peerId: peerId)]) + ) + |> mapToSignal { availableReactions, combinedView -> Signal in + guard let cachedDataView = combinedView.views[.cachedPeerData(peerId: peerId)] as? CachedPeerDataView, let cachedData = cachedDataView.cachedPeerData as? CachedChannelData else { + return .complete() + } + + var reactions: [MessageReaction.Reaction] = [] + var isEnabled = false + + let allowedReactions = cachedData.allowedReactions.knownValue + if let allowedReactions { + switch allowedReactions { + case .all: + isEnabled = true + if let availableReactions { + reactions = availableReactions.reactions.filter({ $0.isEnabled }).map(\.value) + } + case let .limited(list): + isEnabled = true + reactions.append(contentsOf: list) + case .empty: + isEnabled = false + } + } + + var missingReactionFiles: [Int64] = [] + for reaction in reactions { + if let availableReactions, let _ = availableReactions.reactions.filter({ $0.isEnabled }).first(where: { $0.value == reaction }) { + } else { + if case let .custom(fileId) = reaction { + if !missingReactionFiles.contains(fileId) { + missingReactionFiles.append(fileId) + } + } + } + } + + return context.engine.stickers.resolveInlineStickers(fileIds: missingReactionFiles) + |> map { files -> Content in + var result: [EmojiComponentReactionItem] = [] + + for reaction in reactions { + if let availableReactions, let item = availableReactions.reactions.filter({ $0.isEnabled }).first(where: { $0.value == reaction }) { + result.append(EmojiComponentReactionItem(reaction: reaction, file: item.selectAnimation)) + } else { + if case let .custom(fileId) = reaction { + if let file = files[fileId] { + result.append(EmojiComponentReactionItem(reaction: reaction, file: file)) + } + } + } + } + + return Content(isEnabled: isEnabled, enabledReactions: result, availableReactions: availableReactions, allowedReactions: allowedReactions) + } + } + |> distinctUntilChanged + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/BUILD b/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/BUILD new file mode 100644 index 00000000000..36d9e699d32 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/BUILD @@ -0,0 +1,29 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "PeerInfoCoverComponent", + module_name = "PeerInfoCoverComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/Display", + "//submodules/TelegramCore", + "//submodules/Postbox", + "//submodules/TelegramPresentationData", + "//submodules/AccountContext", + "//submodules/ComponentFlow", + "//submodules/TelegramUI/Components/MultiAnimationRenderer", + "//submodules/TelegramUI/Components/AnimationCache", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/TelegramUI/Components/EmojiTextAttachmentView", + "//submodules/Utils/LokiRng", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift new file mode 100644 index 00000000000..855a87fe758 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift @@ -0,0 +1,418 @@ +import Foundation +import AsyncDisplayKit +import Display +import ComponentFlow +import ComponentDisplayAdapters +import AnimationCache +import MultiAnimationRenderer +import TelegramCore +import AccountContext +import SwiftSignalKit +import EmojiTextAttachmentView +import LokiRng + +private final class PatternContentsTarget: MultiAnimationRenderTarget { + private let imageUpdated: (Bool) -> Void + + init(imageUpdated: @escaping (Bool) -> Void) { + self.imageUpdated = imageUpdated + + super.init() + } + + required init(coder: NSCoder) { + preconditionFailure() + } + + override func transitionToContents(_ contents: AnyObject, didLoop: Bool) { + let hadContents = self.contents != nil + self.contents = contents + self.imageUpdated(hadContents) + } +} + +private func windowFunction(t: CGFloat) -> CGFloat { + return bezierPoint(0.6, 0.0, 0.4, 1.0, t) +} + +private func patternScaleValueAt(fraction: CGFloat, t: CGFloat, reverse: Bool) -> CGFloat { + let windowSize: CGFloat = 0.8 + + let effectiveT: CGFloat + let windowStartOffset: CGFloat + let windowEndOffset: CGFloat + if reverse { + effectiveT = 1.0 - t + windowStartOffset = 1.0 + windowEndOffset = -windowSize + } else { + effectiveT = t + windowStartOffset = -windowSize + windowEndOffset = 1.0 + } + + let windowPosition = (1.0 - fraction) * windowStartOffset + fraction * windowEndOffset + let windowT = max(0.0, min(windowSize, effectiveT - windowPosition)) / windowSize + let localT = 1.0 - windowFunction(t: windowT) + + return localT +} + +public final class PeerInfoCoverComponent: Component { + public let context: AccountContext + public let peer: EnginePeer? + public let files: [Int64: TelegramMediaFile] + public let isDark: Bool + public let avatarCenter: CGPoint + public let avatarScale: CGFloat + public let defaultHeight: CGFloat + public let avatarTransitionFraction: CGFloat + public let patternTransitionFraction: CGFloat + + public init( + context: AccountContext, + peer: EnginePeer?, + files: [Int64: TelegramMediaFile], + isDark: Bool, + avatarCenter: CGPoint, + avatarScale: CGFloat, + defaultHeight: CGFloat, + avatarTransitionFraction: CGFloat, + patternTransitionFraction: CGFloat + ) { + self.context = context + self.peer = peer + self.files = files + self.isDark = isDark + self.avatarCenter = avatarCenter + self.avatarScale = avatarScale + self.defaultHeight = defaultHeight + self.avatarTransitionFraction = avatarTransitionFraction + self.patternTransitionFraction = patternTransitionFraction + } + + public static func ==(lhs: PeerInfoCoverComponent, rhs: PeerInfoCoverComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.peer != rhs.peer { + return false + } + if lhs.files != rhs.files { + return false + } + if lhs.isDark != rhs.isDark { + return false + } + if lhs.avatarCenter != rhs.avatarCenter { + return false + } + if lhs.avatarScale != rhs.avatarScale { + return false + } + if lhs.defaultHeight != rhs.defaultHeight { + return false + } + if lhs.avatarTransitionFraction != rhs.avatarTransitionFraction { + return false + } + if lhs.patternTransitionFraction != rhs.patternTransitionFraction { + return false + } + return true + } + + public final class View: UIView { + private let backgroundView: UIView + private let backgroundGradientLayer: SimpleGradientLayer + private let avatarBackgroundPatternContentsLayer: SimpleGradientLayer + private let avatarBackgroundPatternMaskLayer: SimpleLayer + private let avatarBackgroundGradientLayer: SimpleGradientLayer + private let backgroundPatternContainer: UIView + + private var component: PeerInfoCoverComponent? + private var state: EmptyComponentState? + + private var patternContentsTarget: PatternContentsTarget? + private var avatarPatternContentLayers: [SimpleLayer] = [] + private var patternFile: TelegramMediaFile? + private var patternFileDisposable: Disposable? + private var patternImageDisposable: Disposable? + + override public init(frame: CGRect) { + self.backgroundView = UIView() + self.backgroundGradientLayer = SimpleGradientLayer() + + self.avatarBackgroundGradientLayer = SimpleGradientLayer() + let baseAvatarGradientAlpha: CGFloat = 0.4 + let numSteps = 6 + self.avatarBackgroundGradientLayer.colors = (0 ..< numSteps).map { i in + let step: CGFloat = 1.0 - CGFloat(i) / CGFloat(numSteps - 1) + return UIColor(white: 1.0, alpha: baseAvatarGradientAlpha * pow(step, 2.0)).cgColor + } + self.avatarBackgroundGradientLayer.startPoint = CGPoint(x: 0.5, y: 0.5) + self.avatarBackgroundGradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0) + self.avatarBackgroundGradientLayer.type = .radial + + self.avatarBackgroundPatternContentsLayer = SimpleGradientLayer() + self.avatarBackgroundPatternContentsLayer.startPoint = CGPoint(x: 0.5, y: 0.5) + self.avatarBackgroundPatternContentsLayer.endPoint = CGPoint(x: 1.0, y: 1.0) + self.avatarBackgroundPatternContentsLayer.type = .radial + + self.avatarBackgroundPatternMaskLayer = SimpleLayer() + self.backgroundPatternContainer = UIView() + + super.init(frame: frame) + + self.clipsToBounds = true + + self.addSubview(self.backgroundView) + self.layer.addSublayer(self.backgroundGradientLayer) + self.layer.addSublayer(self.avatarBackgroundGradientLayer) + + self.avatarBackgroundPatternContentsLayer.mask = self.avatarBackgroundPatternMaskLayer + self.layer.addSublayer(self.avatarBackgroundPatternContentsLayer) + + self.addSubview(self.backgroundPatternContainer) + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.patternFileDisposable?.dispose() + self.patternImageDisposable?.dispose() + } + + private func loadPatternFromFile() { + guard let component = self.component else { + return + } + guard let patternContentsTarget = self.patternContentsTarget else { + return + } + guard let patternFile = self.patternFile else { + return + } + + if component.context.animationRenderer.loadFirstFrameSynchronously(target: patternContentsTarget, cache: component.context.animationCache, itemId: patternFile.resource.id.stringRepresentation, size: CGSize(width: 96, height: 96)) { + self.updatePatternLayerImages(animated: false) + } else { + let animated = self.patternContentsTarget?.contents == nil + self.patternImageDisposable = component.context.animationRenderer.loadFirstFrame( + target: patternContentsTarget, + cache: component.context.animationCache, + itemId: patternFile.resource.id.stringRepresentation, + size: CGSize(width: 96, height: 96), + fetch: animationCacheFetchFile( + postbox: component.context.account.postbox, + userLocation: .other, + userContentType: .sticker, + resource: .media(media: .standalone(media: patternFile), resource: patternFile.resource), + type: AnimationCacheAnimationType(file: patternFile), + keyframeOnly: false, + customColor: .white + ), + completion: { [weak self] _, _ in + guard let self else { + return + } + self.updatePatternLayerImages(animated: animated) + } + ) + } + } + + private func updatePatternLayerImages(animated: Bool) { + let image = self.patternContentsTarget?.contents + for patternContentLayer in self.avatarPatternContentLayers { + patternContentLayer.contents = image + + if image != nil && animated { + patternContentLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + } + } + } + + func update(component: PeerInfoCoverComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + if self.component?.peer?.profileBackgroundEmojiId != component.peer?.profileBackgroundEmojiId { + if let profileBackgroundEmojiId = component.peer?.profileBackgroundEmojiId, profileBackgroundEmojiId != 0 { + if self.patternContentsTarget == nil { + self.patternContentsTarget = PatternContentsTarget(imageUpdated: { [weak self] hadContents in + guard let self else { + return + } + self.updatePatternLayerImages(animated: !hadContents) + }) + } + + self.patternFile = nil + self.patternFileDisposable?.dispose() + self.patternFileDisposable = nil + self.patternImageDisposable?.dispose() + + let fileId = profileBackgroundEmojiId + if let file = component.files[fileId] { + self.patternFile = file + self.loadPatternFromFile() + } else { + self.patternFileDisposable = (component.context.engine.stickers.resolveInlineStickers(fileIds: [fileId]) + |> deliverOnMainQueue).startStrict(next: { [weak self] files in + guard let self else { + return + } + if let file = files[fileId] { + self.patternFile = file + self.loadPatternFromFile() + } + }) + } + } else { + self.patternContentsTarget = nil + self.patternFileDisposable?.dispose() + self.patternFileDisposable = nil + self.patternFile = nil + self.updatePatternLayerImages(animated: false) + } + } + + self.component = component + self.state = state + + let backgroundColor: UIColor + let secondaryBackgroundColor: UIColor + + if let peer = component.peer, let colors = peer._asPeer().profileColor.flatMap({ component.context.peerNameColors.getProfile($0, dark: component.isDark) }) { + + backgroundColor = colors.main + secondaryBackgroundColor = colors.secondary ?? colors.main + } else { + backgroundColor = .clear + secondaryBackgroundColor = .clear + } + + self.backgroundView.backgroundColor = secondaryBackgroundColor + + self.backgroundGradientLayer.startPoint = CGPoint(x: 0.5, y: 1.0) + self.backgroundGradientLayer.endPoint = CGPoint(x: 0.5, y: 0.0) + self.backgroundGradientLayer.type = .axial + self.backgroundGradientLayer.colors = [backgroundColor.cgColor, secondaryBackgroundColor.cgColor] + self.backgroundGradientLayer.anchorPoint = CGPoint(x: 0.0, y: 1.0) + + let gradientHeight: CGFloat = component.defaultHeight + let backgroundGradientFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - gradientHeight), size: CGSize(width: availableSize.width, height: gradientHeight)) + if !transition.animation.isImmediate { + let previousPosition = self.backgroundGradientLayer.position + let updatedPosition = CGPoint(x: backgroundGradientFrame.minX, y: backgroundGradientFrame.maxY) + self.backgroundGradientLayer.bounds = CGRect(origin: CGPoint(), size: backgroundGradientFrame.size) + self.backgroundGradientLayer.position = updatedPosition + transition.containedViewLayoutTransition.animatePositionAdditive(layer: self.backgroundGradientLayer, offset: CGPoint(x: previousPosition.x - updatedPosition.x, y: previousPosition.y - updatedPosition.y)) + } else { + self.backgroundGradientLayer.frame = backgroundGradientFrame + } + + let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -1000.0 + availableSize.height), size: CGSize(width: availableSize.width, height: 1000.0)) + transition.containedViewLayoutTransition.updateFrameAdditive(view: self.backgroundView, frame: backgroundFrame) + + /*let avatarBackgroundPatternContainerFrame = CGSize(width: 0.0, height: 0.0).centered(around: component.avatarCenter) + transition.containedViewLayoutTransition.updateFrameAdditive(view: self.avatarBackgroundPatternContainer, frame: avatarBackgroundPatternContainerFrame) + transition.containedViewLayoutTransition.updateSublayerTransformScaleAdditive(layer: self.avatarBackgroundPatternContainer.layer, scale: component.avatarScale)*/ + + //transition.setFrame(view: self.avatarBackgroundPatternView, frame: CGSize(width: 200.0, height: 200.0).centered(around: CGPoint())) + + let avatarPatternFrame = CGSize(width: 380.0, height: floor(component.defaultHeight * 1.0)).centered(around: component.avatarCenter) + transition.setFrame(layer: self.avatarBackgroundPatternContentsLayer, frame: avatarPatternFrame) + + if component.peer?.profileColor != nil { + self.avatarBackgroundPatternContentsLayer.compositingFilter = "overlayBlendMode" + self.avatarBackgroundPatternContentsLayer.colors = [ + UIColor(white: 0.0, alpha: 0.6).cgColor, + UIColor(white: 0.0, alpha: 0.0).cgColor + ] + + } else { + self.avatarBackgroundPatternContentsLayer.compositingFilter = nil + let baseWhite: CGFloat = component.isDark ? 0.5 : 0.3 + + self.avatarBackgroundPatternContentsLayer.colors = [ + UIColor(white: baseWhite, alpha: 0.6).cgColor, + UIColor(white: baseWhite, alpha: 0.0).cgColor + ] + } + + self.avatarBackgroundGradientLayer.isHidden = component.peer?.profileColor == nil + transition.setFrame(layer: self.avatarBackgroundGradientLayer, frame: CGSize(width: 300.0, height: 300.0).centered(around: component.avatarCenter)) + transition.setAlpha(layer: self.avatarBackgroundGradientLayer, alpha: 1.0 - component.avatarTransitionFraction) + + let backgroundPatternContainerFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height), size: CGSize(width: availableSize.width, height: 0.0)) + transition.containedViewLayoutTransition.updateFrameAdditive(view: self.backgroundPatternContainer, frame: backgroundPatternContainerFrame) + if component.peer?.id == component.context.account.peerId { + transition.setAlpha(view: self.backgroundPatternContainer, alpha: 0.0) + } else { + transition.setAlpha(view: self.backgroundPatternContainer, alpha: component.patternTransitionFraction) + } + + var avatarBackgroundPatternLayerCount = 0 + let lokiRng = LokiRng(seed0: 123, seed1: 0, seed2: 0) + let numRows = 5 + for row in 0 ..< numRows { + let avatarPatternCount: Int = 7 + let avatarPatternAngleSpan: CGFloat = CGFloat.pi * 2.0 / CGFloat(avatarPatternCount - 1) + + for i in 0 ..< avatarPatternCount - 1 { + let baseItemDistance: CGFloat = 72.0 + CGFloat(row) * 28.0 + + let itemDistanceFraction = max(0.0, min(1.0, baseItemDistance / 140.0)) + let itemScaleFraction = patternScaleValueAt(fraction: component.avatarTransitionFraction, t: itemDistanceFraction, reverse: false) + let itemDistance = baseItemDistance * (1.0 - itemScaleFraction) + 20.0 * itemScaleFraction + + var itemAngle: CGFloat + itemAngle = -CGFloat.pi * 0.5 + CGFloat(i) * avatarPatternAngleSpan + if row % 2 != 0 { + itemAngle += avatarPatternAngleSpan * 0.5 + } + let itemPosition = CGPoint(x: avatarPatternFrame.width * 0.5 + cos(itemAngle) * itemDistance, y: avatarPatternFrame.height * 0.5 + sin(itemAngle) * itemDistance) + + var itemScale: CGFloat + itemScale = 0.7 + CGFloat(lokiRng.next()) * (1.0 - 0.7) + + let itemSize: CGFloat = floor(26.0 * itemScale) + let itemFrame = CGSize(width: itemSize, height: itemSize).centered(around: itemPosition) + + let itemLayer: SimpleLayer + if self.avatarPatternContentLayers.count > avatarBackgroundPatternLayerCount { + itemLayer = self.avatarPatternContentLayers[avatarBackgroundPatternLayerCount] + } else { + itemLayer = SimpleLayer() + itemLayer.contents = self.patternContentsTarget?.contents + self.avatarBackgroundPatternMaskLayer.addSublayer(itemLayer) + self.avatarPatternContentLayers.append(itemLayer) + } + + itemLayer.frame = itemFrame + itemLayer.layerTintColor = UIColor(white: 0.0, alpha: 0.8).cgColor + transition.setAlpha(layer: itemLayer, alpha: 1.0 - itemScaleFraction) + + avatarBackgroundPatternLayerCount += 1 + } + } + if avatarBackgroundPatternLayerCount > self.avatarPatternContentLayers.count { + for i in avatarBackgroundPatternLayerCount ..< self.avatarPatternContentLayers.count { + self.avatarPatternContentLayers[i].removeFromSuperlayer() + } + self.avatarPatternContentLayers.removeSubrange(avatarBackgroundPatternLayerCount ..< self.avatarPatternContentLayers.count) + } + + return availableSize + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD new file mode 100644 index 00000000000..ab5e6546408 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD @@ -0,0 +1,149 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +NGDEPS = [ + "//Nicegram/NGData:NGData", + "//Nicegram/NGEnv:NGEnv", + "//Nicegram/NGLab:NGLab", + "//Nicegram/NGStrings:NGStrings", + "//Nicegram/NGUI:NGUI", + "//Nicegram/NGWebUtils:NGWebUtils", + "@swiftpkg_nicegram_assistant_ios//:Sources_FeatPremiumUI", + "@swiftpkg_nicegram_assistant_ios//:Sources_NGAiChatUI", +] + +swift_library( + name = "PeerInfoScreen", + module_name = "PeerInfoScreen", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = NGDEPS + [ + "//submodules/AccountContext", + "//submodules/AccountUtils", + "//submodules/ActionSheetPeerItem", + "//submodules/ActivityIndicator", + "//submodules/TelegramUI/Components/AnimationCache", + "//submodules/AnimationUI", + "//submodules/AppBundle", + "//submodules/AsyncDisplayKit", + "//submodules/TelegramUI/Components/AvatarEditorScreen", + "//submodules/AvatarNode", + "//submodules/TelegramUI/Components/Stories/AvatarStoryIndicatorComponent", + "//submodules/AvatarVideoNode", + "//submodules/CalendarMessageScreen", + "//submodules/CallListUI", + "//submodules/TelegramUI/Components/Chat/ChatAvatarNavigationNode", + "//submodules/TelegramUI/Components/ChatControllerInteraction", + "//submodules/ChatInterfaceState", + "//submodules/TelegramUI/Components/ChatListHeaderComponent", + "//submodules/ChatListUI", + "//submodules/TelegramUI/Components/Chat/ChatMessageItemView", + "//submodules/ChatPresentationInterfaceState", + "//submodules/TelegramUI/Components/ChatTimerScreen", + "//submodules/TelegramUI/Components/ChatTitleView", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/ComponentFlow", + "//submodules/ContextUI", + "//submodules/PeerInfoUI/CreateExternalMediaStreamScreen", + "//submodules/DeviceAccess", + "//submodules/Display", + "//submodules/TelegramUI/Components/EmojiStatusComponent", + "//submodules/TelegramUI/Components/EmojiStatusSelectionComponent", + "//submodules/EncryptionKeyVisualization", + "//submodules/TelegramUI/Components/EntityKeyboard", + "//submodules/TelegramUI/Components/ForumCreateTopicScreen", + "//submodules/GalleryData", + "//submodules/GalleryUI", + "//submodules/Geocoding", + "//submodules/HashtagSearchUI", + "//submodules/InstantPageCache", + "//submodules/InviteLinksUI", + "//submodules/ItemListAddressItem", + "//submodules/ItemListPeerActionItem", + "//submodules/ItemListPeerItem", + "//submodules/ItemListUI", + "//submodules/LegacyComponents", + "//submodules/LegacyMediaPickerUI", + "//submodules/LegacyUI", + "//submodules/ListMessageItem", + "//submodules/LocationResources", + "//submodules/LocationUI", + "//submodules/ManagedAnimationNode", + "//submodules/MapResourceToAvatarSizes", + "//submodules/Markdown", + "//submodules/MediaResources", + "//submodules/MergeLists", + "//submodules/TelegramUI/Components/MultiAnimationRenderer", + "//submodules/TelegramUI/Components/NotificationExceptionsScreen", + "//submodules/NotificationMuteSettingsUI", + "//submodules/TelegramUI/Components/NotificationPeerExceptionController", + "//submodules/NotificationSoundSelectionUI", + "//submodules/OpenInExternalAppUI", + "//submodules/OverlayStatusController", + "//submodules/PassportUI", + "//submodules/PasswordSetupUI", + "//submodules/PaymentMethodUI", + "//submodules/TelegramUI/Components/PeerAllowedReactionsScreen", + "//submodules/PeerAvatarGalleryUI", + "//submodules/PeerInfoAvatarListNode", + "//submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen", + "//submodules/PeerInfoUI", + "//submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode", + "//submodules/TelegramUI/Components/Settings/PeerNameColorScreen", + "//submodules/PeerPresenceStatusManager", + "//submodules/TelegramUI/Components/PeerReportScreen", + "//submodules/PhoneNumberFormat", + "//submodules/PhotoResources", + "//submodules/Postbox", + "//submodules/PremiumUI", + "//submodules/PresentationDataUtils", + "//submodules/QrCodeUI", + "//submodules/RadialStatusNode", + "//submodules/SaveToCameraRoll", + "//submodules/SearchBarNode", + "//submodules/SearchUI", + "//submodules/TelegramUI/Components/SendInviteLinkScreen", + "//submodules/SettingsUI", + "//submodules/ShareController", + "//submodules/TelegramUI/Components/ShareWithPeersScreen", + "//submodules/StatisticsUI", + "//submodules/StickerPackPreviewUI", + "//submodules/StickerResources", + "//submodules/TelegramUI/Components/StorageUsageScreen", + "//submodules/TelegramUI/Components/Stories/StoryContainerScreen", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/TelegramBaseController", + "//submodules/TelegramCallsUI", + "//submodules/TelegramCore", + "//submodules/TelegramIntents", + "//submodules/TelegramNotices", + "//submodules/TelegramPresentationData", + "//submodules/TelegramStringFormatting", + "//submodules/TelegramUIPreferences", + "//submodules/TelegramUniversalVideoContent", + "//submodules/TelegramVoip", + "//submodules/TemporaryCachedPeerDataManager", + "//submodules/TextFormat", + "//submodules/TooltipUI", + "//submodules/TranslateUI", + "//submodules/UndoUI", + "//submodules/MediaPlayer:UniversalMediaPlayer", + "//submodules/WebSearchUI", + "//submodules/WebUI", + "//submodules/TelegramUI/Components/MultiScaleTextNode", + "//submodules/GridMessageSelectionNode", + "//submodules/ChatMessageInteractiveMediaBadge", + "//submodules/SoftwareVideo", + "//submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode", + "//submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode", + "//submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent", + "//submodules/TelegramUI/Components/LottieComponent", + "//submodules/SolidRoundedButtonNode", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/DynamicIslandBlurNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/DynamicIslandBlurNode.swift new file mode 100644 index 00000000000..75dfe458b3d --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/DynamicIslandBlurNode.swift @@ -0,0 +1,133 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import TelegramPresentationData +import AnimationUI +import Display + +class DynamicIslandMaskNode: ASDisplayNode { + var animationNode: AnimationNode? + + var isForum = false { + didSet { + if self.isForum != oldValue { + self.animationNode?.removeFromSupernode() + let animationNode = AnimationNode(animation: "ForumAvatarMask") + self.addSubnode(animationNode) + self.animationNode = animationNode + } + } + } + + override init() { + let animationNode = AnimationNode(animation: "UserAvatarMask") + self.animationNode = animationNode + + super.init() + + self.addSubnode(animationNode) + } + + func update(_ value: CGFloat) { + self.animationNode?.setProgress(value) + } + + var animating = false + + override func layout() { + self.animationNode?.frame = self.bounds + } +} + +class DynamicIslandBlurNode: ASDisplayNode { + private var effectView: UIVisualEffectView? + private let fadeNode = ASDisplayNode() + let gradientNode = ASImageNode() + + private var hierarchyTrackingNode: HierarchyTrackingNode? + + deinit { + self.animator?.stopAnimation(true) + } + + override func didLoad() { + super.didLoad() + + let hierarchyTrackingNode = HierarchyTrackingNode({ [weak self] value in + if !value { + self?.animator?.stopAnimation(true) + self?.animator = nil + } + }) + self.hierarchyTrackingNode = hierarchyTrackingNode + self.addSubnode(hierarchyTrackingNode) + + self.fadeNode.backgroundColor = .black + self.fadeNode.alpha = 0.0 + + self.gradientNode.displaysAsynchronously = false + let gradientImage = generateImage(CGSize(width: 100.0, height: 100.0), rotatedContext: { size, context in + let bounds = CGRect(origin: .zero, size: size) + context.clear(bounds) + + var locations: [CGFloat] = [0.0, 0.87, 1.0] + let colors: [CGColor] = [UIColor(rgb: 0x000000, alpha: 0.0).cgColor, UIColor(rgb: 0x000000, alpha: 0.0).cgColor, UIColor(rgb: 0x000000, alpha: 1.0).cgColor] + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)! + + let endRadius: CGFloat = 90.0 + let center = CGPoint(x: size.width / 2.0, y: size.height / 2.0 + 38.0) + context.drawRadialGradient(gradient, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: endRadius, options: .drawsAfterEndLocation) + }) + self.gradientNode.image = gradientImage + + let effectView = UIVisualEffectView(effect: nil) + self.effectView = effectView + self.view.insertSubview(effectView, at: 0) + + self.addSubnode(self.gradientNode) + self.addSubnode(self.fadeNode) + } + + private var animator: UIViewPropertyAnimator? + + func prepare() -> Bool { + guard self.animator == nil else { + return false + } + let animator = UIViewPropertyAnimator(duration: 1.0, curve: .linear) + self.animator = animator + self.effectView?.effect = nil + animator.addAnimations { [weak self] in + self?.effectView?.effect = UIBlurEffect(style: .dark) + } + return true + } + + func update(_ value: CGFloat) { + let fadeAlpha = min(1.0, max(0.0, -0.25 + value * 1.55)) + if value > 0.0 { + var value = value + let updated = self.prepare() + if value > 0.99 && updated { + value = 0.99 + } + self.animator?.fractionComplete = max(0.0, -0.1 + value * 1.1) + } else { + self.animator?.stopAnimation(true) + self.animator = nil + self.effectView?.effect = nil + } + self.fadeNode.alpha = fadeAlpha + } + + override func layout() { + super.layout() + + self.effectView?.frame = self.bounds + self.fadeNode.frame = self.bounds + + let gradientSize = CGSize(width: 100.0, height: 100.0) + self.gradientNode.frame = CGRect(origin: CGPoint(x: (self.bounds.width - gradientSize.width) / 2.0, y: 0.0), size: gradientSize) + } +} diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenActionItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenActionItem.swift similarity index 100% rename from submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenActionItem.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenActionItem.swift diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenAddressItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenAddressItem.swift similarity index 100% rename from submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenAddressItem.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenAddressItem.swift diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenCallListItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenCallListItem.swift similarity index 100% rename from submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenCallListItem.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenCallListItem.swift diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenCommentItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenCommentItem.swift similarity index 100% rename from submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenCommentItem.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenCommentItem.swift diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenContactInfoItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenContactInfoItem.swift similarity index 100% rename from submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenContactInfoItem.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenContactInfoItem.swift diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenDisclosureEncryptionKeyItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenDisclosureEncryptionKeyItem.swift similarity index 100% rename from submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenDisclosureEncryptionKeyItem.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenDisclosureEncryptionKeyItem.swift diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenDisclosureItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenDisclosureItem.swift similarity index 80% rename from submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenDisclosureItem.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenDisclosureItem.swift index 59d0c0c0c01..dcf1946b95c 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenDisclosureItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenDisclosureItem.swift @@ -10,36 +10,39 @@ final class PeerInfoScreenDisclosureItem: PeerInfoScreenItem { case badge(String, UIColor) case semitransparentBadge(String, UIColor) case titleBadge(String, UIColor) + case image(UIImage, CGSize) var text: String { switch self { - case .none: - return "" + case .none, .image: + return "" case let .text(text), let .badge(text, _), let .semitransparentBadge(text, _), let .titleBadge(text, _): - return text + return text } } var badgeColor: UIColor? { switch self { - case .none, .text: - return nil - case let .badge(_, color), let .semitransparentBadge(_, color), let .titleBadge(_, color): - return color + case .none, .text, .image: + return nil + case let .badge(_, color), let .semitransparentBadge(_, color), let .titleBadge(_, color): + return color } } } let id: AnyHashable let label: Label + let additionalBadgeLabel: String? let text: String let icon: UIImage? let iconSignal: Signal? let action: (() -> Void)? - init(id: AnyHashable, label: Label = .none, text: String, icon: UIImage? = nil, iconSignal: Signal? = nil, action: (() -> Void)?) { + init(id: AnyHashable, label: Label = .none, additionalBadgeLabel: String? = nil, text: String, icon: UIImage? = nil, iconSignal: Signal? = nil, action: (() -> Void)?) { self.id = id self.label = label + self.additionalBadgeLabel = additionalBadgeLabel self.text = text self.icon = icon self.iconSignal = iconSignal @@ -57,6 +60,7 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode { private let iconNode: ASImageNode private let labelBadgeNode: ASImageNode private let labelNode: ImmediateTextNode + private var additionalLabelNode: ImmediateTextNode? private let textNode: ImmediateTextNode private let arrowNode: ASImageNode private let bottomSeparatorNode: ASDisplayNode @@ -206,7 +210,13 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode { } var badgeDiameter: CGFloat = 20.0 - if case let .semitransparentBadge(text, badgeColor) = item.label, !text.isEmpty { + if case let .image(image, imageSize) = item.label { + self.labelBadgeNode.image = image + badgeDiameter = imageSize.height + if self.labelBadgeNode.supernode == nil { + self.insertSubnode(self.labelBadgeNode, belowSubnode: self.labelNode) + } + } else if case let .semitransparentBadge(text, badgeColor) = item.label, !text.isEmpty { badgeDiameter = 24.0 if previousItem?.label.badgeColor != badgeColor { self.labelBadgeNode.image = generateStretchableFilledCircleImage(diameter: badgeDiameter, color: badgeColor.withAlphaComponent(0.1)) @@ -228,6 +238,13 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode { if self.labelBadgeNode.supernode == nil { self.insertSubnode(self.labelBadgeNode, belowSubnode: self.labelNode) } + } else if item.additionalBadgeLabel != nil { + if previousItem?.additionalBadgeLabel == nil { + self.labelBadgeNode.image = generateFilledRoundedRectImage(size: CGSize(width: 16.0, height: 16.0), cornerRadius: 5.0, color: presentationData.theme.list.itemCheckColors.fillColor)?.stretchableImage(withLeftCapWidth: 6, topCapHeight: 6) + } + if self.labelBadgeNode.supernode == nil { + self.insertSubnode(self.labelBadgeNode, belowSubnode: self.labelNode) + } } else { self.labelBadgeNode.removeFromSupernode() } @@ -247,9 +264,32 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode { labelFrame = CGRect(origin: CGPoint(x: width - rightInset - labelSize.width, y: 12.0), size: labelSize) } + if let additionalBadgeLabel = item.additionalBadgeLabel { + let additionalLabelNode: ImmediateTextNode + if let current = self.additionalLabelNode { + additionalLabelNode = current + } else { + additionalLabelNode = ImmediateTextNode() + additionalLabelNode.isUserInteractionEnabled = false + self.additionalLabelNode = additionalLabelNode + self.addSubnode(additionalLabelNode) + } + + additionalLabelNode.attributedText = NSAttributedString(string: additionalBadgeLabel, font: Font.medium(11.0), textColor: presentationData.theme.list.itemCheckColors.foregroundColor) + let additionalLabelSize = additionalLabelNode.updateLayout(CGSize(width: labelConstrainWidth, height: .greatestFiniteMagnitude)) + additionalLabelNode.frame = CGRect(origin: CGPoint(x: textFrame.maxX + 10.0, y: floor((height - additionalLabelSize.height) / 2.0) + 1.0), size: additionalLabelSize) + } else if let additionalLabelNode = self.additionalLabelNode { + self.additionalLabelNode = nil + additionalLabelNode.removeFromSupernode() + } + let labelBadgeNodeFrame: CGRect - if case .titleBadge = item.label { + if case let .image(_, imageSize) = item.label { + labelBadgeNodeFrame = CGRect(origin: CGPoint(x: width - rightInset - imageSize.width, y: floorToScreenPixels(textFrame.midY - imageSize.height / 2.0)), size:imageSize) + } else if case .titleBadge = item.label { labelBadgeNodeFrame = labelFrame.insetBy(dx: -4.0, dy: -2.0 + UIScreenPixel) + } else if let additionalLabelNode = self.additionalLabelNode { + labelBadgeNodeFrame = additionalLabelNode.frame.insetBy(dx: -4.0, dy: -2.0 + UIScreenPixel) } else { labelBadgeNodeFrame = CGRect(origin: CGPoint(x: width - rightInset - badgeWidth, y: floorToScreenPixels(labelFrame.midY - badgeDiameter / 2.0)), size: CGSize(width: badgeWidth, height: badgeDiameter)) } diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenHeaderItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenHeaderItem.swift similarity index 100% rename from submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenHeaderItem.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenHeaderItem.swift diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenInfoItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenInfoItem.swift similarity index 100% rename from submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenInfoItem.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenInfoItem.swift diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenLabeledValueItem.swift similarity index 100% rename from submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenLabeledValueItem.swift diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenMemberItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenMemberItem.swift similarity index 100% rename from submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenMemberItem.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenMemberItem.swift diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenSelectableBackgroundNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenSelectableBackgroundNode.swift similarity index 100% rename from submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenSelectableBackgroundNode.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenSelectableBackgroundNode.swift diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenSwitchItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenSwitchItem.swift similarity index 100% rename from submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenSwitchItem.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenSwitchItem.swift diff --git a/submodules/TelegramUI/Sources/PeerInfoGifPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGifPaneNode.swift similarity index 100% rename from submodules/TelegramUI/Sources/PeerInfoGifPaneNode.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGifPaneNode.swift diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGroupsInCommonPaneNode.swift similarity index 96% rename from submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGroupsInCommonPaneNode.swift index 7033542b75b..01c912103e5 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGroupsInCommonPaneNode.swift @@ -44,7 +44,7 @@ private struct GroupsInCommonListEntry: Comparable, Identifiable { }, removePeer: { _ in }, contextAction: { node, gesture in openPeerContextAction(peer, node, gesture) - }, hasTopStripe: false, noInsets: true, noCorners: true) + }, hasTopStripe: false, noInsets: true, noCorners: true, style: .plain) } } @@ -62,7 +62,7 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode { private let context: AccountContext private let peerId: PeerId private let chatControllerInteraction: ChatControllerInteraction - private let openPeerContextAction: (Peer, ASDisplayNode, ContextGesture?) -> Void + private let openPeerContextAction: (Bool, Peer, ASDisplayNode, ContextGesture?) -> Void private let groupsInCommonContext: GroupsInCommonContext weak var parentController: ViewController? @@ -99,7 +99,7 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode { private var disposable: Disposable? - init(context: AccountContext, peerId: PeerId, chatControllerInteraction: ChatControllerInteraction, openPeerContextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void, groupsInCommonContext: GroupsInCommonContext) { + init(context: AccountContext, peerId: PeerId, chatControllerInteraction: ChatControllerInteraction, openPeerContextAction: @escaping (Bool, Peer, ASDisplayNode, ContextGesture?) -> Void, groupsInCommonContext: GroupsInCommonContext) { self.context = context self.peerId = peerId self.chatControllerInteraction = chatControllerInteraction @@ -190,7 +190,7 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode { let transaction = preparedTransition(from: self.currentEntries, to: entries, context: self.context, presentationData: presentationData, openPeer: { [weak self] peer in self?.chatControllerInteraction.openPeer(EnginePeer(peer), .default, nil, .default) }, openPeerContextAction: { [weak self] peer, node, gesture in - self?.openPeerContextAction(peer, node, gesture) + self?.openPeerContextAction(false, peer, node, gesture) }) self.currentEntries = entries self.enqueuedTransactions.append(transaction) diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoListPaneNode.swift similarity index 97% rename from submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoListPaneNode.swift index b91dd7792ca..49574358fa9 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoListPaneNode.swift @@ -78,7 +78,7 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode { self.selectedMessages = chatControllerInteraction.selectionState.flatMap { $0.selectedIds } self.selectedMessagesPromise.set(.single(self.selectedMessages)) - self.listNode = ChatHistoryListNode(context: context, updatedPresentationData: updatedPresentationData ?? (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: nil, controllerInteraction: chatControllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), mode: .list(search: false, reversed: false, reverseGroups: false, displayHeaders: .allButLast, hintLinks: tagMask == .webPage, isGlobalSearch: false)) + self.listNode = context.sharedContext.makeChatHistoryListNode(context: context, updatedPresentationData: updatedPresentationData ?? (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, source: .default, subject: nil, controllerInteraction: chatControllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), mode: .list(search: false, reversed: false, reverseGroups: false, displayHeaders: .allButLast, hintLinks: tagMask == .webPage, isGlobalSearch: false)) self.listNode.clipsToBounds = true self.listNode.defaultToSynchronousTransactionWhileScrolling = true self.listNode.scroller.bounces = false diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoMembersPane.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift similarity index 100% rename from submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoMembersPane.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoRecommendedChannelsPane.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoRecommendedChannelsPane.swift new file mode 100644 index 00000000000..ce226d65bfe --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoRecommendedChannelsPane.swift @@ -0,0 +1,401 @@ +import AsyncDisplayKit +import Display +import ComponentFlow +import TelegramCore +import SwiftSignalKit +import Postbox +import TelegramPresentationData +import AccountContext +import ContextUI +import PhotoResources +import TelegramUIPreferences +import ItemListPeerItem +import ItemListPeerActionItem +import MergeLists +import ItemListUI +import PeerInfoVisualMediaPaneNode +import ChatControllerInteraction +import MultilineTextComponent +import Markdown +import SolidRoundedButtonNode + +private struct RecommendedChannelsListTransaction { + let deletions: [ListViewDeleteItem] + let insertions: [ListViewInsertItem] + let updates: [ListViewUpdateItem] + let animated: Bool +} + +private enum RecommendedChannelsListEntryStableId: Hashable { + case addMember + case peer(PeerId) +} + +private enum RecommendedChannelsListEntry: Comparable, Identifiable { + case peer(theme: PresentationTheme, index: Int, peer: EnginePeer, subscribers: Int32) + + var stableId: RecommendedChannelsListEntryStableId { + switch self { + case let .peer(_, _, peer, _): + return .peer(peer.id) + } + } + + static func ==(lhs: RecommendedChannelsListEntry, rhs: RecommendedChannelsListEntry) -> Bool { + switch lhs { + case let .peer(lhsTheme, lhsIndex, lhsPeer, lhsSubscribers): + if case let .peer(rhsTheme, rhsIndex, rhsPeer, rhsSubscribers) = rhs, lhsTheme === rhsTheme, lhsIndex == rhsIndex, lhsPeer == rhsPeer, lhsSubscribers == rhsSubscribers { + return true + } else { + return false + } + } + } + + static func <(lhs: RecommendedChannelsListEntry, rhs: RecommendedChannelsListEntry) -> Bool { + switch lhs { + case let .peer(_, lhsIndex, _, _): + switch rhs { + case let .peer(_, rhsIndex, _, _): + return lhsIndex < rhsIndex + } + } + } + + func item(context: AccountContext, presentationData: PresentationData, action: @escaping (EnginePeer) -> Void, openPeerContextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void) -> ListViewItem { + switch self { + case let .peer(_, _, peer, subscribers): + let subtitle = presentationData.strings.Conversation_StatusSubscribers(subscribers) + return ItemListPeerItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: context, peer: peer, presence: nil, text: .text(subtitle, .secondary), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, selectable: true, sectionId: 0, action: { + action(peer) + }, setPeerIdWithRevealedOptions: { _, _ in + }, removePeer: { _ in + }, contextAction: { node, gesture in + openPeerContextAction(peer._asPeer(), node, gesture) + }, hasTopStripe: false, noInsets: true, noCorners: true, style: .plain, disableInteractiveTransitionIfNecessary: true) + } + } +} + +private func preparedTransition(from fromEntries: [RecommendedChannelsListEntry], to toEntries: [RecommendedChannelsListEntry], context: AccountContext, presentationData: PresentationData, action: @escaping (EnginePeer) -> Void, openPeerContextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void) -> RecommendedChannelsListTransaction { + let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) + + let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, action: action, openPeerContextAction: openPeerContextAction), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, action: action, openPeerContextAction: openPeerContextAction), directionHint: nil) } + + return RecommendedChannelsListTransaction(deletions: deletions, insertions: insertions, updates: updates, animated: toEntries.count < fromEntries.count) +} + +private let channelsLimit: Int32 = 8 + +final class PeerInfoRecommendedChannelsPaneNode: ASDisplayNode, PeerInfoPaneNode { + private let context: AccountContext + private let chatControllerInteraction: ChatControllerInteraction + private let openPeerContextAction: (Bool, Peer, ASDisplayNode, ContextGesture?) -> Void + + weak var parentController: ViewController? + + private let listNode: ListView + private var currentEntries: [RecommendedChannelsListEntry] = [] + private var currentState: (RecommendedChannels?, Bool)? + private var canLoadMore: Bool = false + private var enqueuedTransactions: [RecommendedChannelsListTransaction] = [] + + private var unlockBackground: UIImageView? + private var unlockText: ComponentView? + private var unlockButton: SolidRoundedButtonNode? + + private var currentParams: (size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, isScrollingLockedAtTop: Bool, presentationData: PresentationData)? + + private var theme: PresentationTheme? + private let presentationDataPromise = Promise() + + private let ready = Promise() + private var didSetReady: Bool = false + var isReady: Signal { + return self.ready.get() + } + + private let statusPromise = Promise(nil) + var status: Signal { + self.statusPromise.get() + } + + var tabBarOffsetUpdated: ((ContainedViewLayoutTransition) -> Void)? + var tabBarOffset: CGFloat { + return 0.0 + } + + private var disposable: Disposable? + + init(context: AccountContext, peerId: PeerId, chatControllerInteraction: ChatControllerInteraction, openPeerContextAction: @escaping (Bool, Peer, ASDisplayNode, ContextGesture?) -> Void) { + self.context = context + self.chatControllerInteraction = chatControllerInteraction + self.openPeerContextAction = openPeerContextAction + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + self.listNode = ListView() + self.listNode.accessibilityPageScrolledString = { row, count in + return presentationData.strings.VoiceOver_ScrollStatus(row, count).string + } + + super.init() + + self.listNode.preloadPages = true + self.addSubnode(self.listNode) + + self.disposable = (combineLatest(queue: .mainQueue(), + self.presentationDataPromise.get(), + context.engine.peers.recommendedChannels(peerId: peerId), + context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) + |> map { peer -> Bool in + return peer?.isPremium ?? false + } + ) + |> deliverOnMainQueue).startStrict(next: { [weak self] presentationData, recommendedChannels, isPremium in + guard let strongSelf = self else { + return + } + strongSelf.currentState = (recommendedChannels, isPremium) + strongSelf.updateState(recommendedChannels: recommendedChannels, isPremium: isPremium, presentationData: presentationData) + }) + + self.statusPromise.set(context.engine.data.subscribe( + TelegramEngine.EngineData.Item.Peer.ParticipantCount(id: peerId) + ) + |> map { count -> PeerInfoStatusData? in + if let count { + return PeerInfoStatusData(text: presentationData.strings.Conversation_StatusSubscribers(Int32(count)), isActivity: true, key: .recommended) + } + return nil + }) + } + + deinit { + self.disposable?.dispose() + } + + func ensureMessageIsVisible(id: MessageId) { + } + + func scrollToTop() -> Bool { + if !self.listNode.scrollToOffsetFromTop(0.0, animated: true) { + self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + return true + } else { + return false + } + } + + func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { + let isFirstLayout = self.currentParams == nil + self.currentParams = (size, sideInset, bottomInset, isScrollingLockedAtTop, presentationData) + self.presentationDataPromise.set(.single(presentationData)) + + transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(), size: size)) + let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition) + + var scrollToItem: ListViewScrollToItem? + if isScrollingLockedAtTop { + switch self.listNode.visibleContentOffset() { + case let .known(value) where value <= CGFloat.ulpOfOne: + break + default: + scrollToItem = ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Spring(duration: duration), directionHint: .Up) + } + } + self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: scrollToItem, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: size, insets: UIEdgeInsets(top: topInset, left: sideInset, bottom: bottomInset, right: sideInset), duration: duration, curve: curve), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + + self.listNode.scrollEnabled = !isScrollingLockedAtTop + + if isFirstLayout, let (recommendedChannels, isPremium) = self.currentState { + self.updateState(recommendedChannels: recommendedChannels, isPremium: isPremium, presentationData: presentationData) + } + } + + @objc private func unlockPressed() { + let controller = self.context.sharedContext.makePremiumIntroController(context: self.context, source: .ads, forceDark: false, dismissed: nil) + self.chatControllerInteraction.navigationController()?.pushViewController(controller) + } + + private func updateState(recommendedChannels: RecommendedChannels?, isPremium: Bool, presentationData: PresentationData) { + var entries: [RecommendedChannelsListEntry] = [] + + if let channels = recommendedChannels?.channels { + for channel in channels { + entries.append(.peer(theme: presentationData.theme, index: entries.count, peer: channel.peer, subscribers: channel.subscribers)) + } + } + + let transaction = preparedTransition(from: self.currentEntries, to: entries, context: self.context, presentationData: presentationData, action: { [weak self] peer in + self?.chatControllerInteraction.openPeer(peer, .default, nil, .default) + }, openPeerContextAction: { [weak self] peer, node, gesture in + self?.openPeerContextAction(true, peer, node, gesture) + }) + self.currentEntries = entries + self.enqueuedTransactions.append(transaction) + self.dequeueTransaction() + + self.layoutUnlockPanel() + } + + private func layoutUnlockPanel() { + guard let (_, isPremium) = self.currentState, let currentParams = self.currentParams else { + return + } + if !isPremium { + let size = currentParams.size + let sideInset = currentParams.sideInset + let bottomInset = currentParams.bottomInset + let presentationData = currentParams.presentationData + + let themeUpdated = self.theme !== presentationData.theme + self.theme = presentationData.theme + + let unlockText: ComponentView + let unlockBackground: UIImageView + let unlockButton: SolidRoundedButtonNode + if let current = self.unlockText { + unlockText = current + } else { + unlockText = ComponentView() + self.unlockText = unlockText + } + + if let current = self.unlockBackground { + unlockBackground = current + } else { + unlockBackground = UIImageView() + unlockBackground.contentMode = .scaleToFill + self.view.addSubview(unlockBackground) + self.unlockBackground = unlockBackground + } + + if let current = self.unlockButton { + unlockButton = current + } else { + unlockButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: presentationData.theme), height: 50.0, cornerRadius: 10.0) + self.view.addSubview(unlockButton.view) + self.unlockButton = unlockButton + + unlockButton.animationLoopTime = 2.5 + unlockButton.animation = "premium_unlock" + unlockButton.iconPosition = .right + unlockButton.title = presentationData.strings.Channel_SimilarChannels_ShowMore + + unlockButton.pressed = { [weak self] in + self?.unlockPressed() + } + } + + if themeUpdated { + let topColor = presentationData.theme.list.plainBackgroundColor.withAlphaComponent(0.0) + let bottomColor = presentationData.theme.list.plainBackgroundColor + unlockBackground.image = generateGradientImage(size: CGSize(width: 1.0, height: 170.0), colors: [topColor, bottomColor, bottomColor], locations: [0.0, 0.3, 1.0]) + unlockButton.updateTheme(SolidRoundedButtonTheme(theme: presentationData.theme)) + } + + let textFont = Font.regular(15.0) + let boldTextFont = Font.semibold(15.0) + let textColor = presentationData.theme.list.itemSecondaryTextColor + let linkColor = presentationData.theme.list.itemAccentColor + let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: boldTextFont, textColor: linkColor), linkAttribute: { _ in + return nil + }) + + var scrollOffset: CGFloat = 0.0 + if case let .known(offset) = self.listNode.visibleBottomContentOffset() { + scrollOffset = min(0.0, offset + bottomInset + 80.0) + } + + let unlockSize = unlockText.update( + transition: .immediate, + component: AnyComponent( + MultilineTextComponent( + text: .markdown(text: presentationData.strings.Channel_SimilarChannels_ShowMoreInfo, attributes: markdownAttributes), + horizontalAlignment: .center, + maximumNumberOfLines: 0, + lineSpacing: 0.2 + ) + ), + environment: {}, + containerSize: CGSize(width: size.width - 32.0, height: 200.0) + ) + if let view = unlockText.view { + if view.superview == nil { + view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.unlockPressed))) + self.view.addSubview(view) + } + view.frame = CGRect(origin: CGPoint(x: floor((size.width - unlockSize.width) / 2.0), y: size.height - bottomInset - unlockSize.height - 13.0 + scrollOffset), size: unlockSize) + } + + unlockBackground.frame = CGRect(x: 0.0, y: size.height - bottomInset - 170.0 + scrollOffset, width: size.width, height: bottomInset + 170.0) + + let buttonSideInset = sideInset + 16.0 + let buttonSize = CGSize(width: size.width - buttonSideInset * 2.0, height: 50.0) + unlockButton.frame = CGRect(origin: CGPoint(x: buttonSideInset, y: size.height - bottomInset - unlockSize.height - buttonSize.height - 26.0 + scrollOffset), size: buttonSize) + let _ = unlockButton.updateLayout(width: buttonSize.width, transition: .immediate) + } else { + self.unlockBackground?.removeFromSuperview() + self.unlockBackground = nil + + self.unlockButton?.view.removeFromSuperview() + self.unlockButton = nil + + self.unlockText?.view?.removeFromSuperview() + self.unlockText = nil + } + } + + private func dequeueTransaction() { + guard let _ = self.currentParams, let transaction = self.enqueuedTransactions.first else { + return + } + + self.enqueuedTransactions.remove(at: 0) + + var options = ListViewDeleteAndInsertOptions() + if transaction.animated { + options.insert(.AnimateInsertion) + } else { + options.insert(.Synchronous) + } + + self.listNode.transaction(deleteIndices: transaction.deletions, insertIndicesAndItems: transaction.insertions, updateIndicesAndItems: transaction.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in + guard let strongSelf = self else { + return + } + if !strongSelf.didSetReady { + strongSelf.didSetReady = true + strongSelf.ready.set(.single(true)) + } + }) + } + + func findLoadedMessage(id: MessageId) -> Message? { + return nil + } + + func updateHiddenMedia() { + } + + func transferVelocity(_ velocity: CGFloat) { + if velocity > 0.0 { + self.listNode.transferVelocity(velocity) + } + } + + func cancelPreviewGestures() { + } + + func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? { + return nil + } + + func addToTransitionSurface(view: UIView) { + } + + func updateSelectedMessages(animated: Bool) { + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarListNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarListNode.swift new file mode 100644 index 00000000000..56e7f7f9106 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarListNode.swift @@ -0,0 +1,179 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import TelegramPresentationData +import PeerInfoAvatarListNode +import SwiftSignalKit +import Postbox +import TelegramCore +import ContextUI +import AccountContext +import Display + +final class PeerInfoAvatarListNode: ASDisplayNode { + private let isSettings: Bool + let containerNode: ASDisplayNode + let pinchSourceNode: PinchSourceContainerNode + let bottomCoverNode: ASDisplayNode + let maskNode: DynamicIslandMaskNode + let topCoverNode: DynamicIslandBlurNode + let avatarContainerNode: PeerInfoAvatarTransformContainerNode + let listContainerTransformNode: ASDisplayNode + let listContainerNode: PeerInfoAvatarListContainerNode + + let isReady = Promise() + + var arguments: (Peer?, Int64?, EngineMessageHistoryThread.Info?, PresentationTheme, CGFloat, Bool)? + var item: PeerInfoAvatarListItem? + + var itemsUpdated: (([PeerInfoAvatarListItem]) -> Void)? + var animateOverlaysFadeIn: (() -> Void)? + var openStories: (() -> Void)? + + init(context: AccountContext, readyWhenGalleryLoads: Bool, isSettings: Bool) { + self.isSettings = isSettings + + self.containerNode = ASDisplayNode() + + self.bottomCoverNode = ASDisplayNode() + + self.maskNode = DynamicIslandMaskNode() + self.pinchSourceNode = PinchSourceContainerNode() + + self.avatarContainerNode = PeerInfoAvatarTransformContainerNode(context: context) + self.listContainerTransformNode = ASDisplayNode() + self.listContainerNode = PeerInfoAvatarListContainerNode(context: context, isSettings: isSettings) + self.listContainerNode.clipsToBounds = true + self.listContainerNode.isHidden = true + + self.topCoverNode = DynamicIslandBlurNode() + + super.init() + + self.addSubnode(self.containerNode) + self.containerNode.addSubnode(self.bottomCoverNode) + self.containerNode.addSubnode(self.pinchSourceNode) + self.pinchSourceNode.contentNode.addSubnode(self.avatarContainerNode) + self.listContainerTransformNode.addSubnode(self.listContainerNode) + self.pinchSourceNode.contentNode.addSubnode(self.listContainerTransformNode) + self.containerNode.addSubnode(self.topCoverNode) + + let avatarReady = (self.avatarContainerNode.avatarNode.ready + |> mapToSignal { _ -> Signal in + return .complete() + } + |> then(.single(true))) + + let galleryReady = self.listContainerNode.isReady.get() + |> filter { value in + return value + } + |> take(1) + + let combinedSignal: Signal + if readyWhenGalleryLoads { + combinedSignal = combineLatest(queue: .mainQueue(), + avatarReady, + galleryReady + ) + |> map { lhs, rhs in + return lhs && rhs + } + } else { + combinedSignal = avatarReady + } + + self.isReady.set(combinedSignal + |> filter { value in + return value + } + |> take(1)) + + self.listContainerNode.itemsUpdated = { [weak self] items in + if let strongSelf = self { + strongSelf.item = items.first + strongSelf.itemsUpdated?(items) + if let (peer, threadId, threadInfo, theme, avatarSize, isExpanded) = strongSelf.arguments { + strongSelf.avatarContainerNode.update(peer: peer, threadId: threadId, threadInfo: threadInfo, item: strongSelf.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded, isSettings: strongSelf.isSettings) + } + } + } + + self.pinchSourceNode.activate = { [weak self] sourceNode in + guard let strongSelf = self, let (_, _, _, _, _, isExpanded) = strongSelf.arguments, isExpanded else { + return + } + let pinchController = PinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: { + return UIScreen.main.bounds + }) + context.sharedContext.mainWindow?.presentInGlobalOverlay(pinchController) + + strongSelf.listContainerNode.bottomShadowNode.alpha = 0.0 + strongSelf.listContainerNode.contentNode.updateIsInPinchMode(true) + } + + self.pinchSourceNode.animatedOut = { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.animateOverlaysFadeIn?() + strongSelf.listContainerNode.contentNode.updateIsInPinchMode(false) + } + + self.listContainerNode.openStories = { [weak self] in + guard let self else { + return + } + self.openStories?() + } + } + + func update(size: CGSize, avatarSize: CGFloat, isExpanded: Bool, peer: Peer?, isForum: Bool, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, theme: PresentationTheme, transition: ContainedViewLayoutTransition) { + self.arguments = (peer, threadId, threadInfo, theme, avatarSize, isExpanded) + self.maskNode.isForum = isForum + self.pinchSourceNode.update(size: size, transition: transition) + self.containerNode.frame = CGRect(origin: CGPoint(), size: size) + self.pinchSourceNode.frame = CGRect(origin: CGPoint(), size: size) + self.avatarContainerNode.update(peer: peer, threadId: threadId, threadInfo: threadInfo, item: self.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded, isSettings: self.isSettings) + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if !self.listContainerNode.isHidden { + if let result = self.listContainerNode.view.hitTest(self.view.convert(point, to: self.listContainerNode.view), with: event) { + return result + } + } else { + if let result = self.avatarContainerNode.avatarNode.view.hitTest(self.view.convert(point, to: self.avatarContainerNode.avatarNode.view), with: event) { + return result + } else if let result = self.avatarContainerNode.iconView?.view?.hitTest(self.view.convert(point, to: self.avatarContainerNode.iconView?.view), with: event) { + return result + } + } + + return super.hitTest(point, with: event) + } + + func animateAvatarCollapse(transition: ContainedViewLayoutTransition) { + if let currentItemNode = self.listContainerNode.currentItemNode, case .animated = transition { + if let _ = self.avatarContainerNode.videoNode { + + } else if let _ = self.avatarContainerNode.markupNode { + + } else if let unroundedImage = self.avatarContainerNode.avatarNode.unroundedImage { + let avatarCopyView = UIImageView() + avatarCopyView.image = unroundedImage + avatarCopyView.frame = self.avatarContainerNode.avatarNode.frame + avatarCopyView.center = currentItemNode.imageNode.position + currentItemNode.view.addSubview(avatarCopyView) + let scale = currentItemNode.imageNode.bounds.height / avatarCopyView.bounds.height + avatarCopyView.layer.transform = CATransform3DMakeScale(scale, scale, scale) + avatarCopyView.alpha = 0.0 + transition.updateAlpha(layer: avatarCopyView.layer, alpha: 1.0, completion: { [weak avatarCopyView] _ in + Queue.mainQueue().after(0.1, { + avatarCopyView?.removeFromSuperview() + }) + }) + } + } + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarTransformContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarTransformContainerNode.swift new file mode 100644 index 00000000000..7eb9cac1652 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarTransformContainerNode.swift @@ -0,0 +1,435 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import ContextUI +import TelegramPresentationData +import AccountContext +import AvatarNode +import UniversalMediaPlayer +import Display +import ComponentFlow +import UniversalMediaPlayer +import AvatarVideoNode +import SwiftSignalKit +import TelegramUniversalVideoContent +import PeerInfoAvatarListNode +import Postbox +import TelegramCore +import EmojiStatusComponent +import GalleryUI + + +final class PeerInfoAvatarTransformContainerNode: ASDisplayNode { + let context: AccountContext + + let containerNode: ContextControllerSourceNode + + let avatarNode: AvatarNode + private(set) var avatarStoryView: ComponentView? + var videoNode: UniversalVideoNode? + var markupNode: AvatarVideoNode? + var iconView: ComponentView? + private var videoContent: NativeVideoContent? + private var videoStartTimestamp: Double? + + var isExpanded: Bool = false + var canAttachVideo: Bool = true { + didSet { + if oldValue != self.canAttachVideo { + self.videoNode?.canAttachContent = !self.isExpanded && self.canAttachVideo + } + } + } + + var tapped: (() -> Void)? + var emojiTapped: (() -> Void)? + var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? + + private var isFirstAvatarLoading = true + var item: PeerInfoAvatarListItem? + + private let playbackStartDisposable = MetaDisposable() + + var storyData: (totalCount: Int, unseenCount: Int, hasUnseenCloseFriends: Bool)? + var storyProgress: Float? + + init(context: AccountContext) { + self.context = context + self.containerNode = ContextControllerSourceNode() + + let avatarFont = avatarPlaceholderFont(size: floor(100.0 * 16.0 / 37.0)) + self.avatarNode = AvatarNode(font: avatarFont) + + super.init() + + self.addSubnode(self.containerNode) + self.containerNode.addSubnode(self.avatarNode) + self.containerNode.frame = CGRect(origin: CGPoint(x: -50.0, y: -50.0), size: CGSize(width: 100.0, height: 100.0)) + self.avatarNode.frame = self.containerNode.bounds + + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))) + self.avatarNode.view.addGestureRecognizer(tapGestureRecognizer) + + self.containerNode.activated = { [weak self] gesture, _ in + guard let strongSelf = self else { + return + } + tapGestureRecognizer.isEnabled = false + tapGestureRecognizer.isEnabled = true + strongSelf.contextAction?(strongSelf.containerNode, gesture) + } + } + + deinit { + self.playbackStartDisposable.dispose() + } + + func updateStoryView(transition: ContainedViewLayoutTransition, theme: PresentationTheme, peer: Peer?) { + var colors = AvatarNode.Colors(theme: theme) + + let regularNavigationContentsSecondaryColor: UIColor + if let profileColor = peer?.profileColor { + let backgroundColors = self.context.peerNameColors.getProfile(profileColor, dark: theme.overallDarkAppearance) + regularNavigationContentsSecondaryColor = UIColor(white: 1.0, alpha: 0.6).blitOver(backgroundColors.main.withMultiplied(hue: 1.0, saturation: 2.2, brightness: 1.5), alpha: 1.0) + + let storyColors = self.context.peerNameColors.getProfile(profileColor, dark: theme.overallDarkAppearance, subject: .stories) + + var unseenColors: [UIColor] = [storyColors.main] + if let secondary = storyColors.secondary { + unseenColors.insert(secondary, at: 0) + } + colors.unseenColors = unseenColors + colors.unseenCloseFriendsColors = colors.unseenColors + colors.seenColors = colors.unseenColors + } else { + regularNavigationContentsSecondaryColor = theme.list.controlSecondaryColor + } + + colors.seenColors = [ + regularNavigationContentsSecondaryColor, + regularNavigationContentsSecondaryColor + ] + + var storyStats: AvatarNode.StoryStats? + if let storyData = self.storyData { + storyStats = AvatarNode.StoryStats( + totalCount: storyData.totalCount, + unseenCount: storyData.unseenCount, + hasUnseenCloseFriendsItems: storyData.hasUnseenCloseFriends, + progress: self.storyProgress + ) + } else if let storyProgress = self.storyProgress { + storyStats = AvatarNode.StoryStats( + totalCount: 1, + unseenCount: 1, + hasUnseenCloseFriendsItems: false, + progress: storyProgress + ) + } + self.avatarNode.setStoryStats(storyStats: storyStats, presentationParams: AvatarNode.StoryPresentationParams( + colors: colors, + lineWidth: 3.0, + inactiveLineWidth: 1.5 + ), transition: Transition(transition)) + } + + @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.tapped?() + } + } + + @objc private func emojiTapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.emojiTapped?() + } + } + + func updateTransitionFraction(_ fraction: CGFloat, transition: ContainedViewLayoutTransition) { + if let videoNode = self.videoNode { + if case .immediate = transition, fraction == 1.0 { + return + } + if fraction > 0.0 { + videoNode.pause() + } else { + videoNode.play() + } + transition.updateAlpha(node: videoNode, alpha: 1.0 - fraction) + } + if let markupNode = self.markupNode { + if case .immediate = transition, fraction == 1.0 { + return + } + if fraction > 0.0 { + markupNode.updateVisibility(false) + } else { + markupNode.updateVisibility(true) + } + transition.updateAlpha(node: markupNode, alpha: 1.0 - fraction) + } + } + + var removedPhotoResourceIds = Set() + func update(peer: Peer?, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool, isSettings: Bool) { + if let peer = peer { + let previousItem = self.item + var item = item + self.item = item + + var overrideImage: AvatarNodeImageOverride? + if peer.isDeleted { + overrideImage = .deletedIcon + } else if let previousItem = previousItem, item == nil { + if case let .image(_, representations, _, _, _, _) = previousItem, let rep = representations.last { + self.removedPhotoResourceIds.insert(rep.representation.resource.id.stringRepresentation) + } + overrideImage = AvatarNodeImageOverride.none + item = nil + } else if let rep = peer.profileImageRepresentations.last, self.removedPhotoResourceIds.contains(rep.resource.id.stringRepresentation) { + overrideImage = AvatarNodeImageOverride.none + item = nil + } + + if let _ = overrideImage { + self.containerNode.isGestureEnabled = false + } else if peer.profileImageRepresentations.isEmpty { + self.containerNode.isGestureEnabled = false + } else { + self.containerNode.isGestureEnabled = false + } + + self.avatarNode.imageNode.animateFirstTransition = !isSettings + self.avatarNode.setPeer(context: self.context, theme: theme, peer: EnginePeer(peer), overrideImage: overrideImage, clipStyle: .none, synchronousLoad: self.isFirstAvatarLoading, displayDimensions: CGSize(width: avatarSize, height: avatarSize), storeUnrounded: true) + + if let threadInfo = threadInfo { + self.avatarNode.isHidden = true + + let iconView: ComponentView + if let current = self.iconView { + iconView = current + } else { + iconView = ComponentView() + self.iconView = iconView + } + let content: EmojiStatusComponent.Content + if threadId == 1 { + content = .image(image: PresentationResourcesChat.chatGeneralThreadIcon(theme)) + } else if let iconFileId = threadInfo.icon { + content = .animation(content: .customEmoji(fileId: iconFileId), size: CGSize(width: avatarSize, height: avatarSize), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: theme.list.itemAccentColor, loopMode: .forever) + } else { + content = .topic(title: String(threadInfo.title.prefix(1)), color: threadInfo.iconColor, size: CGSize(width: avatarSize, height: avatarSize)) + } + let _ = iconView.update( + transition: .immediate, + component: AnyComponent(EmojiStatusComponent( + context: self.context, + animationCache: self.context.animationCache, + animationRenderer: self.context.animationRenderer, + content: content, + isVisibleForAnimations: true, + action: nil + )), + environment: {}, + containerSize: CGSize(width: avatarSize, height: avatarSize) + ) + if let iconComponentView = iconView.view { + iconComponentView.isUserInteractionEnabled = true + if iconComponentView.superview == nil { + iconComponentView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.emojiTapGesture(_:)))) + self.avatarNode.view.superview?.addSubview(iconComponentView) + } + iconComponentView.frame = CGRect(origin: CGPoint(), size: CGSize(width: avatarSize, height: avatarSize)) + } + } + + var isForum = false + let avatarCornerRadius: CGFloat + if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) { + avatarCornerRadius = floor(avatarSize * 0.25) + isForum = true + } else { + avatarCornerRadius = avatarSize / 2.0 + } + if self.avatarNode.layer.cornerRadius != 0.0 { + ContainedViewLayoutTransition.animated(duration: 0.3, curve: .easeInOut).updateCornerRadius(layer: self.avatarNode.contentNode.layer, cornerRadius: avatarCornerRadius) + } else { + self.avatarNode.contentNode.layer.cornerRadius = avatarCornerRadius + } + self.avatarNode.contentNode.layer.masksToBounds = true + + self.isFirstAvatarLoading = false + + self.containerNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize)) + self.avatarNode.frame = self.containerNode.bounds + self.avatarNode.font = avatarPlaceholderFont(size: floor(avatarSize * 16.0 / 37.0)) + + if let item = item { + let representations: [ImageRepresentationWithReference] + let videoRepresentations: [VideoRepresentationWithReference] + let immediateThumbnailData: Data? + var videoId: Int64 + let markup: TelegramMediaImage.EmojiMarkup? + switch item { + case .custom: + representations = [] + videoRepresentations = [] + immediateThumbnailData = nil + videoId = 0 + markup = nil + case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail): + representations = topRepresentations + videoRepresentations = videoRepresentationsValue + immediateThumbnailData = immediateThumbnail + videoId = peer.id.id._internalGetInt64Value() + if let resource = videoRepresentations.first?.representation.resource as? CloudPhotoSizeMediaResource { + videoId = videoId &+ resource.photoId + } + markup = nil + case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail, _, markupValue): + representations = imageRepresentations + videoRepresentations = videoRepresentationsValue + immediateThumbnailData = immediateThumbnail + if case let .cloud(imageId, _, _) = reference { + videoId = imageId + } else { + videoId = peer.id.id._internalGetInt64Value() + } + markup = markupValue + } + + self.containerNode.isGestureEnabled = !isSettings + + if let markup { + if let videoNode = self.videoNode { + self.videoContent = nil + self.videoStartTimestamp = nil + self.videoNode = nil + + videoNode.removeFromSupernode() + } + + let markupNode: AvatarVideoNode + if let current = self.markupNode { + markupNode = current + } else { + markupNode = AvatarVideoNode(context: self.context) + self.avatarNode.contentNode.addSubnode(markupNode) + self.markupNode = markupNode + } + markupNode.update(markup: markup, size: CGSize(width: 320.0, height: 320.0)) + markupNode.updateVisibility(true) + } else if threadInfo == nil, let video = videoRepresentations.last, let peerReference = PeerReference(peer) { + let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [], preloadSize: nil)])) + let videoContent = NativeVideoContent(id: .profileVideo(videoId, nil), userLocation: .other, fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: peer.isCopyProtectionEnabled, storeAfterDownload: nil) + if videoContent.id != self.videoContent?.id { + self.videoNode?.removeFromSupernode() + + let mediaManager = self.context.sharedContext.mediaManager + let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded) + videoNode.isUserInteractionEnabled = false + videoNode.isHidden = true + + if let startTimestamp = video.representation.startTimestamp { + self.videoStartTimestamp = startTimestamp + self.playbackStartDisposable.set((videoNode.status + |> map { status -> Bool in + if let status = status, case .playing = status.status { + return true + } else { + return false + } + } + |> filter { playing in + return playing + } + |> take(1) + |> deliverOnMainQueue).start(completed: { [weak self] in + if let strongSelf = self { + Queue.mainQueue().after(0.15) { + strongSelf.videoNode?.isHidden = false + } + } + })) + } else { + self.videoStartTimestamp = nil + self.playbackStartDisposable.set(nil) + videoNode.isHidden = false + } + + self.videoContent = videoContent + self.videoNode = videoNode + + let maskPath: UIBezierPath + if isForum { + maskPath = UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size), cornerRadius: avatarCornerRadius) + } else { + maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size)) + } + let shape = CAShapeLayer() + shape.path = maskPath.cgPath + videoNode.layer.mask = shape + + self.avatarNode.contentNode.addSubnode(videoNode) + } + } else { + if let markupNode = self.markupNode { + self.markupNode = nil + markupNode.removeFromSupernode() + } + if let videoNode = self.videoNode { + self.videoStartTimestamp = nil + self.videoContent = nil + self.videoNode = nil + + videoNode.removeFromSupernode() + } + } + } else { + if let markupNode = self.markupNode { + self.markupNode = nil + markupNode.removeFromSupernode() + } + if let videoNode = self.videoNode { + self.videoStartTimestamp = nil + self.videoContent = nil + self.videoNode = nil + + videoNode.removeFromSupernode() + } + self.containerNode.isGestureEnabled = false + } + + if let markupNode = self.markupNode { + markupNode.frame = self.avatarNode.bounds + markupNode.updateLayout(size: self.avatarNode.bounds.size, cornerRadius: avatarCornerRadius, transition: .immediate) + } + + if let videoNode = self.videoNode { + if self.canAttachVideo { + videoNode.updateLayout(size: self.avatarNode.frame.size, transition: .immediate) + } + videoNode.frame = self.avatarNode.contentNode.bounds + + if isExpanded == videoNode.canAttachContent { + self.isExpanded = isExpanded + let update = { + videoNode.canAttachContent = !self.isExpanded && self.canAttachVideo + if videoNode.canAttachContent { + videoNode.play() + } + } + if isExpanded { + DispatchQueue.main.async { + update() + } + } else { + update() + } + } + } + } + + self.updateStoryView(transition: .immediate, theme: theme, peer: peer) + } +} diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift similarity index 98% rename from submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift index aae88ad4646..d9b5551ad86 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift @@ -406,7 +406,7 @@ private func peerInfoScreenInputData(context: AccountContext, peerId: EnginePeer } } -func keepPeerInfoScreenDataHot(context: AccountContext, peerId: PeerId, chatLocation: ChatLocation, chatLocationContextHolder: Atomic) -> Signal { +public func keepPeerInfoScreenDataHot(context: AccountContext, peerId: PeerId, chatLocation: ChatLocation, chatLocationContextHolder: Atomic) -> Signal { return peerInfoScreenInputData(context: context, peerId: peerId, isSettings: false) |> mapToSignal { inputData -> Signal in switch inputData { @@ -822,8 +822,15 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen availablePanes = nil } + var peer: Peer? + peer = peerView.peers[userPeerId] + + /*if let user = peer as? TelegramUser, let profileColor = user.nameColor { + peer = user.withUpdatedProfileColor(PeerNameColor(rawValue: profileColor.rawValue)).withUpdatedProfileBackgroundEmojiId(user.backgroundEmojiId) + }*/ + return PeerInfoScreenData( - peer: peerView.peers[userPeerId], + peer: peer, chatPeer: peerView.peers[peerId], cachedData: peerView.cachedData, status: status, @@ -893,14 +900,18 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen requestsContextPromise.get(), requestsStatePromise.get(), hasStories, - accountIsPremium + accountIsPremium, + context.engine.peers.recommendedChannels(peerId: peerId) ) - |> map { peerView, availablePanes, globalNotificationSettings, status, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories, accountIsPremium -> PeerInfoScreenData in + |> map { peerView, availablePanes, globalNotificationSettings, status, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories, accountIsPremium, recommendedChannels -> PeerInfoScreenData in var availablePanes = availablePanes if let hasStories { if hasStories { availablePanes?.insert(.stories, at: 0) } + if let recommendedChannels, !recommendedChannels.channels.isEmpty { + availablePanes?.append(.recommended) + } } else { availablePanes = nil } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoEditingAvatarNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoEditingAvatarNode.swift new file mode 100644 index 00000000000..909f5357498 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoEditingAvatarNode.swift @@ -0,0 +1,233 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import TelegramPresentationData +import AccountContext +import AvatarNode +import UniversalMediaPlayer +import PeerInfoAvatarListNode +import AvatarVideoNode +import TelegramUniversalVideoContent +import SwiftSignalKit +import Postbox +import TelegramCore +import Display +import GalleryUI + +final class PeerInfoEditingAvatarNode: ASDisplayNode { + private let context: AccountContext + let avatarNode: AvatarNode + fileprivate var videoNode: UniversalVideoNode? + fileprivate var markupNode: AvatarVideoNode? + private var videoContent: NativeVideoContent? + private var videoStartTimestamp: Double? + var item: PeerInfoAvatarListItem? + + var tapped: ((Bool) -> Void)? + + var canAttachVideo: Bool = true + + init(context: AccountContext) { + self.context = context + let avatarFont = avatarPlaceholderFont(size: floor(100.0 * 16.0 / 37.0)) + self.avatarNode = AvatarNode(font: avatarFont) + + super.init() + + self.addSubnode(self.avatarNode) + self.avatarNode.frame = CGRect(origin: CGPoint(x: -50.0, y: -50.0), size: CGSize(width: 100.0, height: 100.0)) + + self.avatarNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) + } + + @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.tapped?(false) + } + } + + func reset() { + guard let videoNode = self.videoNode else { + return + } + videoNode.isHidden = true + videoNode.seek(self.videoStartTimestamp ?? 0.0) + Queue.mainQueue().after(0.15) { + videoNode.isHidden = false + } + } + + var removedPhotoResourceIds = Set() + func update(peer: Peer?, threadData: MessageHistoryThreadData?, chatLocation: ChatLocation, item: PeerInfoAvatarListItem?, updatingAvatar: PeerInfoUpdatingAvatar?, uploadProgress: AvatarUploadProgress?, theme: PresentationTheme, avatarSize: CGFloat, isEditing: Bool) { + guard let peer = peer else { + return + } + + let canEdit = canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData) + + let previousItem = self.item + var item = item + self.item = item + + let overrideImage: AvatarNodeImageOverride? + if canEdit, peer.profileImageRepresentations.isEmpty { + overrideImage = .editAvatarIcon(forceNone: true) + } else if let previousItem = previousItem, item == nil { + if case let .image(_, representations, _, _, _, _) = previousItem, let rep = representations.last { + self.removedPhotoResourceIds.insert(rep.representation.resource.id.stringRepresentation) + } + overrideImage = canEdit ? .editAvatarIcon(forceNone: true) : AvatarNodeImageOverride.none + item = nil + } else if let representation = peer.profileImageRepresentations.last, self.removedPhotoResourceIds.contains(representation.resource.id.stringRepresentation) { + overrideImage = canEdit ? .editAvatarIcon(forceNone: true) : AvatarNodeImageOverride.none + item = nil + } else { + overrideImage = item == nil && canEdit ? .editAvatarIcon(forceNone: true) : nil + } + self.avatarNode.font = avatarPlaceholderFont(size: floor(avatarSize * 16.0 / 37.0)) + self.avatarNode.setPeer(context: self.context, theme: theme, peer: EnginePeer(peer), overrideImage: overrideImage, clipStyle: .none, synchronousLoad: false, displayDimensions: CGSize(width: avatarSize, height: avatarSize)) + self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize)) + + var isForum = false + let avatarCornerRadius: CGFloat + if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) { + isForum = true + avatarCornerRadius = floor(avatarSize * 0.25) + } else { + avatarCornerRadius = avatarSize / 2.0 + } + if self.avatarNode.layer.cornerRadius != 0.0 { + ContainedViewLayoutTransition.animated(duration: 0.3, curve: .easeInOut).updateCornerRadius(layer: self.avatarNode.layer, cornerRadius: avatarCornerRadius) + } else { + self.avatarNode.layer.cornerRadius = avatarCornerRadius + } + self.avatarNode.layer.masksToBounds = true + + if let item = item { + let representations: [ImageRepresentationWithReference] + let videoRepresentations: [VideoRepresentationWithReference] + let immediateThumbnailData: Data? + var videoId: Int64 + let markup: TelegramMediaImage.EmojiMarkup? + switch item { + case .custom: + representations = [] + videoRepresentations = [] + immediateThumbnailData = nil + videoId = 0 + markup = nil + case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail): + representations = topRepresentations + videoRepresentations = videoRepresentationsValue + immediateThumbnailData = immediateThumbnail + videoId = peer.id.id._internalGetInt64Value() + if let resource = videoRepresentations.first?.representation.resource as? CloudPhotoSizeMediaResource { + videoId = videoId &+ resource.photoId + } + markup = nil + case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail, _, markupValue): + representations = imageRepresentations + videoRepresentations = videoRepresentationsValue + immediateThumbnailData = immediateThumbnail + if case let .cloud(imageId, _, _) = reference { + videoId = imageId + } else { + videoId = peer.id.id._internalGetInt64Value() + } + markup = markupValue + } + + if let markup { + if let videoNode = self.videoNode { + self.videoContent = nil + self.videoStartTimestamp = nil + self.videoNode = nil + + videoNode.removeFromSupernode() + } + + let markupNode: AvatarVideoNode + if let current = self.markupNode { + markupNode = current + } else { + markupNode = AvatarVideoNode(context: self.context) + self.avatarNode.contentNode.addSubnode(markupNode) + self.markupNode = markupNode + } + markupNode.update(markup: markup, size: CGSize(width: 320.0, height: 320.0)) + markupNode.updateVisibility(true) + } else if threadData == nil, let video = videoRepresentations.last, let peerReference = PeerReference(peer) { + if let markupNode = self.markupNode { + self.markupNode = nil + markupNode.removeFromSupernode() + } + + let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [], preloadSize: nil)])) + let videoContent = NativeVideoContent(id: .profileVideo(videoId, nil), userLocation: .other, fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: peer.isCopyProtectionEnabled, storeAfterDownload: nil) + if videoContent.id != self.videoContent?.id { + self.videoNode?.removeFromSupernode() + + let mediaManager = self.context.sharedContext.mediaManager + let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .gallery) + videoNode.isUserInteractionEnabled = false + self.videoStartTimestamp = video.representation.startTimestamp + self.videoContent = videoContent + self.videoNode = videoNode + + let maskPath: UIBezierPath + if isForum { + maskPath = UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size), cornerRadius: avatarCornerRadius) + } else { + maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size)) + } + let shape = CAShapeLayer() + shape.path = maskPath.cgPath + videoNode.layer.mask = shape + + self.avatarNode.contentNode.addSubnode(videoNode) + } + } else { + if let markupNode = self.markupNode { + self.markupNode = nil + markupNode.removeFromSupernode() + } + if let videoNode = self.videoNode { + self.videoStartTimestamp = nil + self.videoContent = nil + self.videoNode = nil + + videoNode.removeFromSupernode() + } + } + } else if let videoNode = self.videoNode { + self.videoStartTimestamp = nil + self.videoContent = nil + self.videoNode = nil + + videoNode.removeFromSupernode() + } + + if let markupNode = self.markupNode { + markupNode.frame = self.avatarNode.bounds + markupNode.updateLayout(size: self.avatarNode.bounds.size, cornerRadius: avatarCornerRadius, transition: .immediate) + } + + if let videoNode = self.videoNode { + if self.canAttachVideo { + videoNode.updateLayout(size: self.avatarNode.bounds.size, transition: .immediate) + } + videoNode.frame = self.avatarNode.bounds + + if isEditing != videoNode.canAttachContent { + videoNode.canAttachContent = isEditing && self.canAttachVideo + } + } + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if self.avatarNode.frame.contains(point) { + return self.avatarNode.view + } + return super.hitTest(point, with: event) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoEditingAvatarOverlayNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoEditingAvatarOverlayNode.swift new file mode 100644 index 00000000000..b585b19b989 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoEditingAvatarOverlayNode.swift @@ -0,0 +1,149 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import TelegramPresentationData +import AccountContext +import Display +import RadialStatusNode +import Postbox +import TelegramCore +import PeerInfoAvatarListNode +import AvatarNode +import SwiftSignalKit + +final class PeerInfoEditingAvatarOverlayNode: ASDisplayNode { + private let context: AccountContext + + private let imageNode: ImageNode + private let updatingAvatarOverlay: ASImageNode + private let iconNode: ASImageNode + private var statusNode: RadialStatusNode + + private var currentRepresentation: TelegramMediaImageRepresentation? + + init(context: AccountContext) { + self.context = context + + self.imageNode = ImageNode(enableEmpty: true) + + self.updatingAvatarOverlay = ASImageNode() + self.updatingAvatarOverlay.displayWithoutProcessing = true + self.updatingAvatarOverlay.displaysAsynchronously = false + self.updatingAvatarOverlay.alpha = 0.0 + + self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(rgb: 0x000000, alpha: 0.6)) + self.statusNode.isUserInteractionEnabled = false + + self.iconNode = ASImageNode() + self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Avatar/EditAvatarIconLarge"), color: .white) + self.iconNode.alpha = 0.0 + + super.init() + + self.imageNode.frame = CGRect(origin: CGPoint(x: -50.0, y: -50.0), size: CGSize(width: 100.0, height: 100.0)) + self.updatingAvatarOverlay.frame = self.imageNode.frame + + let radialStatusSize: CGFloat = 50.0 + let imagePosition = self.imageNode.position + self.statusNode.frame = CGRect(origin: CGPoint(x: floor(imagePosition.x - radialStatusSize / 2.0), y: floor(imagePosition.y - radialStatusSize / 2.0)), size: CGSize(width: radialStatusSize, height: radialStatusSize)) + + if let image = self.iconNode.image { + self.iconNode.frame = CGRect(origin: CGPoint(x: floor(imagePosition.x - image.size.width / 2.0), y: floor(imagePosition.y - image.size.height / 2.0)), size: image.size) + } + + self.addSubnode(self.imageNode) + self.addSubnode(self.updatingAvatarOverlay) + self.addSubnode(self.statusNode) + } + + func updateTransitionFraction(_ fraction: CGFloat, transition: ContainedViewLayoutTransition) { + transition.updateAlpha(node: self, alpha: 1.0 - fraction) + } + + func update(peer: Peer?, threadData: MessageHistoryThreadData?, chatLocation: ChatLocation, item: PeerInfoAvatarListItem?, updatingAvatar: PeerInfoUpdatingAvatar?, uploadProgress: AvatarUploadProgress?, theme: PresentationTheme, avatarSize: CGFloat, isEditing: Bool) { + guard let peer = peer else { + return + } + + self.imageNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize)) + self.updatingAvatarOverlay.frame = self.imageNode.frame + + let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .linear) + + let clipStyle: AvatarNodeClipStyle + if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) { + clipStyle = .roundedRect + } else { + clipStyle = .round + } + + var isPersonal = false + if let updatingAvatar, case let .image(image) = updatingAvatar, image.isPersonal { + isPersonal = true + } + + if canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData) + || isPersonal + || self.currentRepresentation != nil && updatingAvatar == nil { + var overlayHidden = true + if let updatingAvatar = updatingAvatar { + overlayHidden = false + + var cancelEnabled = true + let progressValue: CGFloat? + if let uploadProgress { + switch uploadProgress { + case let .value(value): + progressValue = max(0.027, value) + case .indefinite: + progressValue = nil + cancelEnabled = false + } + } else { + progressValue = 0.027 + } + self.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: progressValue, cancelEnabled: cancelEnabled, animateRotation: true)) + + if case let .image(representation) = updatingAvatar { + if representation != self.currentRepresentation { + self.currentRepresentation = representation + + if let signal = peerAvatarImage(account: context.account, peerReference: nil, authorOfMessage: nil, representation: representation, displayDimensions: CGSize(width: avatarSize, height: avatarSize), clipStyle: clipStyle, emptyColor: nil, synchronousLoad: false, provideUnrounded: false) { + self.imageNode.setSignal(signal |> map { $0?.0 }) + } + } + } + + transition.updateAlpha(node: self.updatingAvatarOverlay, alpha: 1.0) + } else { + let targetOverlayAlpha: CGFloat = 0.0 + if self.updatingAvatarOverlay.alpha != targetOverlayAlpha { + let update = { + self.statusNode.transitionToState(.none) + self.currentRepresentation = nil + self.imageNode.setSignal(.single(nil)) + transition.updateAlpha(node: self.updatingAvatarOverlay, alpha: overlayHidden ? 0.0 : 1.0) + } + Queue.mainQueue().after(0.3) { + update() + } + } + } + if !overlayHidden && self.updatingAvatarOverlay.image == nil { + switch clipStyle { + case .round: + self.updatingAvatarOverlay.image = generateFilledCircleImage(diameter: avatarSize, color: UIColor(white: 0.0, alpha: 0.4), backgroundColor: nil) + case .roundedRect: + self.updatingAvatarOverlay.image = generateFilledRoundedRectImage(size: CGSize(width: avatarSize, height: avatarSize), cornerRadius: avatarSize * 0.25, color: UIColor(white: 0.0, alpha: 0.4), backgroundColor: nil) + default: + break + } + } + } else { + self.statusNode.transitionToState(.none) + self.currentRepresentation = nil + transition.updateAlpha(node: self.iconNode, alpha: 0.0) + transition.updateAlpha(node: self.updatingAvatarOverlay, alpha: 0.0) + } + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderActionButtonNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderActionButtonNode.swift new file mode 100644 index 00000000000..e71cf810976 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderActionButtonNode.swift @@ -0,0 +1,88 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import ContextUI +import TelegramPresentationData +import Display + +final class PeerInfoHeaderActionButtonNode: HighlightableButtonNode { + let key: PeerInfoHeaderButtonKey + private let action: (PeerInfoHeaderActionButtonNode, ContextGesture?) -> Void + let referenceNode: ContextReferenceContentNode + let containerNode: ContextControllerSourceNode + private let backgroundNode: ASDisplayNode + private let textNode: ImmediateTextNode + + private var theme: PresentationTheme? + + init(key: PeerInfoHeaderButtonKey, action: @escaping (PeerInfoHeaderActionButtonNode, ContextGesture?) -> Void) { + self.key = key + self.action = action + + self.referenceNode = ContextReferenceContentNode() + self.containerNode = ContextControllerSourceNode() + self.containerNode.animateScale = false + + self.backgroundNode = ASDisplayNode() + self.backgroundNode.cornerRadius = 11.0 + + self.textNode = ImmediateTextNode() + self.textNode.displaysAsynchronously = false + self.textNode.isUserInteractionEnabled = false + + super.init() + + self.accessibilityTraits = .button + + self.containerNode.addSubnode(self.referenceNode) + self.referenceNode.addSubnode(self.backgroundNode) + self.addSubnode(self.containerNode) + self.addSubnode(self.textNode) + + self.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + strongSelf.layer.removeAnimation(forKey: "opacity") + strongSelf.alpha = 0.4 + } else { + strongSelf.alpha = 1.0 + strongSelf.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + } + } + } + + self.containerNode.activated = { [weak self] gesture, _ in + if let strongSelf = self { + strongSelf.action(strongSelf, gesture) + } + } + + self.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) + } + + @objc private func buttonPressed() { + self.action(self, nil) + } + + func update(size: CGSize, text: String, presentationData: PresentationData, transition: ContainedViewLayoutTransition) { + let themeUpdated = self.theme != presentationData.theme + if themeUpdated { + self.theme = presentationData.theme + + self.containerNode.isGestureEnabled = false + + self.backgroundNode.backgroundColor = presentationData.theme.list.itemAccentColor + transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size)) + } + + self.textNode.attributedText = NSAttributedString(string: text, font: Font.semibold(16.0), textColor: presentationData.theme.list.itemCheckColors.foregroundColor) + self.accessibilityLabel = text + let titleSize = self.textNode.updateLayout(CGSize(width: 120.0, height: .greatestFiniteMagnitude)) + + transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: size)) + transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size)) + transition.updateFrameAdditiveToCenter(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: floorToScreenPixels((size.height - titleSize.height) / 2.0)), size: titleSize)) + + self.referenceNode.frame = self.containerNode.bounds + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderButtonNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderButtonNode.swift new file mode 100644 index 00000000000..38474a74ee6 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderButtonNode.swift @@ -0,0 +1,276 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import ContextUI +import AnimationUI +import Display +import TelegramPresentationData +import ComponentFlow +import LottieComponent + +enum PeerInfoHeaderButtonKey: Hashable { + case message + case discussion + case call + case videoCall + case voiceChat + case mute + case more + case addMember + case search + case leave + case stop + case addContact +} + +enum PeerInfoHeaderButtonIcon { + case message + case call + case videoCall + case voiceChat + case mute + case unmute + case more + case addMember + case search + case leave + case stop +} + +final class PeerInfoHeaderButtonNode: HighlightableButtonNode { + let key: PeerInfoHeaderButtonKey + private let action: (PeerInfoHeaderButtonNode, ContextGesture?) -> Void + let referenceNode: ContextReferenceContentNode + let containerNode: ContextControllerSourceNode + private let backgroundNode: NavigationBackgroundNode + private let contentNode: ASDisplayNode + private let iconNode: ASImageNode + private let textNode: ImmediateTextNode + private var animatedIcon: ComponentView? + + private var theme: PresentationTheme? + private var icon: PeerInfoHeaderButtonIcon? + private var isActive: Bool? + + init(key: PeerInfoHeaderButtonKey, action: @escaping (PeerInfoHeaderButtonNode, ContextGesture?) -> Void) { + self.key = key + self.action = action + + self.referenceNode = ContextReferenceContentNode() + self.containerNode = ContextControllerSourceNode() + self.containerNode.animateScale = false + + self.backgroundNode = NavigationBackgroundNode(color: UIColor(white: 1.0, alpha: 0.2), enableBlur: true, enableSaturation: false) + self.backgroundNode.isUserInteractionEnabled = false + + self.contentNode = ASDisplayNode() + self.contentNode.isUserInteractionEnabled = false + + self.iconNode = ASImageNode() + self.iconNode.displaysAsynchronously = false + self.iconNode.displayWithoutProcessing = true + self.iconNode.isUserInteractionEnabled = false + + self.textNode = ImmediateTextNode() + self.textNode.displaysAsynchronously = false + self.textNode.isUserInteractionEnabled = false + + super.init() + + self.accessibilityTraits = .button + + self.containerNode.addSubnode(self.referenceNode) + self.referenceNode.addSubnode(self.backgroundNode) + self.referenceNode.addSubnode(self.contentNode) + self.contentNode.addSubnode(self.iconNode) + self.addSubnode(self.containerNode) + self.contentNode.addSubnode(self.textNode) + + self.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + strongSelf.layer.removeAnimation(forKey: "opacity") + strongSelf.alpha = 0.4 + } else { + strongSelf.alpha = 1.0 + strongSelf.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + } + } + } + + self.containerNode.activated = { [weak self] gesture, _ in + if let strongSelf = self { + strongSelf.action(strongSelf, gesture) + } + } + + self.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) + } + + @objc private func buttonPressed() { + switch self.icon { + case .voiceChat, .more, .leave: + if let animatedIconView = self.animatedIcon?.view as? LottieComponent.View { + animatedIconView.playOnce() + } + default: + break + } + self.action(self, nil) + } + + func update(size: CGSize, text: String, icon: PeerInfoHeaderButtonIcon, isActive: Bool, presentationData: PresentationData, backgroundColor: UIColor, foregroundColor: UIColor, fraction: CGFloat, transition: ContainedViewLayoutTransition) { + let previousIcon = self.icon + let themeUpdated = self.theme != presentationData.theme + let iconUpdated = self.icon != icon + let isActiveUpdated = self.isActive != isActive + self.isActive = isActive + + let iconSize = CGSize(width: 40.0, height: 40.0) + + if themeUpdated || iconUpdated { + self.theme = presentationData.theme + self.icon = icon + + var isGestureEnabled = false + if [.mute, .voiceChat, .more].contains(icon) { + isGestureEnabled = true + } + self.containerNode.isGestureEnabled = isGestureEnabled + + let iconColor = UIColor.white + self.iconNode.image = generateImage(iconSize, contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setBlendMode(.normal) + context.setFillColor(iconColor.cgColor) + let imageName: String? + switch icon { + case .message: + imageName = "Peer Info/ButtonMessage" + case .call: + imageName = "Peer Info/ButtonCall" + case .videoCall: + imageName = "Peer Info/ButtonVideo" + case .voiceChat: + imageName = nil + case .mute: + imageName = nil + case .unmute: + imageName = nil + case .more: + imageName = nil + case .addMember: + imageName = "Peer Info/ButtonAddMember" + case .search: + imageName = "Peer Info/ButtonSearch" + case .leave: + imageName = nil + case .stop: + imageName = "Peer Info/ButtonStop" + } + if let imageName = imageName, let image = generateTintedImage(image: UIImage(bundleImageName: imageName), color: .white) { + let imageRect = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size) + context.clip(to: imageRect, mask: image.cgImage!) + context.fill(imageRect) + } + }) + } + + let animationName: String? + var playOnce = false + var seekToEnd = false + switch icon { + case .voiceChat: + animationName = "anim_profilevc" + case .mute: + animationName = "anim_profileunmute" + if previousIcon == .unmute { + playOnce = true + } else { + seekToEnd = true + } + case .unmute: + animationName = "anim_profilemute" + if previousIcon == .mute { + playOnce = true + } else { + seekToEnd = true + } + case .more: + animationName = "anim_profilemore" + case .leave: + animationName = "anim_profileleave" + default: + animationName = nil + } + + if let animationName = animationName { + let animatedIcon: ComponentView + if let current = self.animatedIcon { + animatedIcon = current + } else { + animatedIcon = ComponentView() + self.animatedIcon = animatedIcon + } + let _ = animatedIcon.update( + transition: .immediate, + component: AnyComponent(LottieComponent( + content: LottieComponent.AppBundleContent(name: animationName), + color: foregroundColor, + startingPosition: seekToEnd ? .end : .begin + )), + environment: {}, + containerSize: iconSize + ) + } else if let animatedIcon = self.animatedIcon { + self.animatedIcon = nil + animatedIcon.view?.removeFromSuperview() + } + + if let animatedIconView = self.animatedIcon?.view as? LottieComponent.View { + if animatedIconView.superview == nil { + self.contentNode.view.addSubview(animatedIconView) + } + if playOnce { + animatedIconView.playOnce() + } + } + + transition.updateTintColor(layer: self.iconNode.layer, color: foregroundColor) + transition.updateTintColor(layer: self.titleNode.layer, color: foregroundColor) + transition.updateTintColor(layer: self.textNode.layer, color: foregroundColor) + + if isActiveUpdated { + let alphaTransition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut) + alphaTransition.updateAlpha(node: self.iconNode, alpha: isActive ? 1.0 : 0.3) + if let animatedIconView = self.animatedIcon?.view { + alphaTransition.updateAlpha(layer: animatedIconView.layer, alpha: isActive ? 1.0 : 0.3) + } + alphaTransition.updateAlpha(node: self.textNode, alpha: isActive ? 1.0 : 0.3) + } + + self.textNode.attributedText = NSAttributedString(string: text.lowercased(), font: Font.regular(11.0), textColor: .white) + self.accessibilityLabel = text + let titleSize = self.textNode.updateLayout(CGSize(width: 120.0, height: .greatestFiniteMagnitude)) + + transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: size)) + transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: size.height * 0.5 * (1.0 - fraction)), size: size)) + transition.updateAlpha(node: self.contentNode, alpha: fraction) + + let backgroundY: CGFloat = size.height * (1.0 - fraction) + let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: backgroundY), size: CGSize(width: size.width, height: max(0.0, size.height - backgroundY))) + transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) + + transition.updateSublayerTransformScale(node: self.contentNode, scale: 1.0 * fraction + 0.001 * (1.0 - fraction)) + + self.backgroundNode.update(size: backgroundFrame.size, cornerRadius: min(11.0, backgroundFrame.height * 0.5), transition: transition) + self.backgroundNode.updateColor(color: backgroundColor, transition: transition) + transition.updateFrame(node: self.iconNode, frame: CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: 1.0), size: iconSize)) + if let animatedIconView = self.animatedIcon?.view { + transition.updateFrame(view: animatedIconView, frame: CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: 1.0), size: iconSize)) + } + transition.updateFrameAdditiveToCenter(node: self.textNode, frame: CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: size.height - titleSize.height - 9.0), size: titleSize)) + + self.referenceNode.frame = self.containerNode.bounds + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderEditingContentNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderEditingContentNode.swift new file mode 100644 index 00000000000..4233007ae91 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderEditingContentNode.swift @@ -0,0 +1,183 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import TelegramPresentationData +import AccountContext +import Display +import Postbox +import TelegramCore +import SwiftSignalKit + +final class PeerInfoHeaderEditingContentNode: ASDisplayNode { + private let context: AccountContext + private let requestUpdateLayout: () -> Void + + var requestEditing: (() -> Void)? + + let avatarNode: PeerInfoEditingAvatarNode + let avatarTextNode: ImmediateTextNode + let avatarButtonNode: HighlightableButtonNode + + var itemNodes: [PeerInfoHeaderTextFieldNodeKey: PeerInfoHeaderTextFieldNode] = [:] + + init(context: AccountContext, requestUpdateLayout: @escaping () -> Void) { + self.context = context + self.requestUpdateLayout = requestUpdateLayout + + self.avatarNode = PeerInfoEditingAvatarNode(context: context) + + self.avatarTextNode = ImmediateTextNode() + self.avatarButtonNode = HighlightableButtonNode() + + super.init() + + self.addSubnode(self.avatarNode) + self.avatarButtonNode.addSubnode(self.avatarTextNode) + + self.avatarButtonNode.addTarget(self, action: #selector(textPressed), forControlEvents: .touchUpInside) + } + + @objc private func textPressed() { + self.requestEditing?() + } + + func editingTextForKey(_ key: PeerInfoHeaderTextFieldNodeKey) -> String? { + return self.itemNodes[key]?.text + } + + func shakeTextForKey(_ key: PeerInfoHeaderTextFieldNodeKey) { + self.itemNodes[key]?.layer.addShakeAnimation() + } + + func update(width: CGFloat, safeInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, peer: Peer?, threadData: MessageHistoryThreadData?, chatLocation: ChatLocation, cachedData: CachedPeerData?, isContact: Bool, isSettings: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) -> CGFloat { + let avatarSize: CGFloat = isModalOverlay ? 200.0 : 100.0 + let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 22.0), size: CGSize(width: avatarSize, height: avatarSize)) + transition.updateFrameAdditiveToCenter(node: self.avatarNode, frame: CGRect(origin: avatarFrame.center, size: CGSize())) + + var contentHeight: CGFloat = statusBarHeight + 10.0 + avatarSize + 20.0 + + if canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData) { + if self.avatarButtonNode.supernode == nil { + self.addSubnode(self.avatarButtonNode) + } + self.avatarTextNode.attributedText = NSAttributedString(string: presentationData.strings.Settings_SetNewProfilePhotoOrVideo, font: Font.regular(17.0), textColor: presentationData.theme.list.itemAccentColor) + self.avatarButtonNode.accessibilityLabel = self.avatarTextNode.attributedText?.string + + let avatarTextSize = self.avatarTextNode.updateLayout(CGSize(width: width, height: 32.0)) + transition.updateFrame(node: self.avatarTextNode, frame: CGRect(origin: CGPoint(), size: avatarTextSize)) + transition.updateFrame(node: self.avatarButtonNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((width - avatarTextSize.width) / 2.0), y: contentHeight - 1.0), size: avatarTextSize)) + contentHeight += 32.0 + } + + var isEditableBot = false + if let user = peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) { + isEditableBot = true + } + var fieldKeys: [PeerInfoHeaderTextFieldNodeKey] = [] + if let user = peer as? TelegramUser { + if !user.isDeleted { + fieldKeys.append(.firstName) + if isEditableBot { + fieldKeys.append(.description) + } else if user.botInfo == nil { + fieldKeys.append(.lastName) + } + } + } else if let _ = peer as? TelegramGroup { + fieldKeys.append(.title) + if canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData) { + fieldKeys.append(.description) + } + } else if let _ = peer as? TelegramChannel { + fieldKeys.append(.title) + if canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData) { + fieldKeys.append(.description) + } + } + var hasPrevious = false + for key in fieldKeys { + let itemNode: PeerInfoHeaderTextFieldNode + var updateText: String? + if let current = self.itemNodes[key] { + itemNode = current + } else { + var isMultiline = false + switch key { + case .firstName: + if let peer = peer as? TelegramUser { + if let editableBotInfo = (cachedData as? CachedUserData)?.editableBotInfo { + updateText = editableBotInfo.name + } else { + updateText = peer.firstName ?? "" + } + } + case .lastName: + updateText = (peer as? TelegramUser)?.lastName ?? "" + case .title: + updateText = peer?.debugDisplayTitle ?? "" + case .description: + isMultiline = true + if let cachedData = cachedData as? CachedChannelData { + updateText = cachedData.about ?? "" + } else if let cachedData = cachedData as? CachedGroupData { + updateText = cachedData.about ?? "" + } else if let cachedData = cachedData as? CachedUserData { + if let editableBotInfo = cachedData.editableBotInfo { + updateText = editableBotInfo.about + } else { + updateText = cachedData.about ?? "" + } + } else { + updateText = "" + } + } + if isMultiline { + itemNode = PeerInfoHeaderMultiLineTextFieldNode(requestUpdateHeight: { [weak self] in + self?.requestUpdateLayout() + }) + } else { + itemNode = PeerInfoHeaderSingleLineTextFieldNode() + } + self.itemNodes[key] = itemNode + self.addSubnode(itemNode) + } + let placeholder: String + var isEnabled = true + switch key { + case .firstName: + placeholder = isEditableBot ? presentationData.strings.UserInfo_BotNamePlaceholder : presentationData.strings.UserInfo_FirstNamePlaceholder + isEnabled = isContact || isSettings || isEditableBot + case .lastName: + placeholder = presentationData.strings.UserInfo_LastNamePlaceholder + isEnabled = isContact || isSettings + case .title: + if let channel = peer as? TelegramChannel, case .broadcast = channel.info { + placeholder = presentationData.strings.GroupInfo_ChannelListNamePlaceholder + } else { + placeholder = presentationData.strings.GroupInfo_GroupNamePlaceholder + } + isEnabled = canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData) + case .description: + placeholder = presentationData.strings.Channel_Edit_AboutItem + isEnabled = canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData) || isEditableBot + } + let itemHeight = itemNode.update(width: width, safeInset: safeInset, isSettings: isSettings, hasPrevious: hasPrevious, hasNext: key != fieldKeys.last, placeholder: placeholder, isEnabled: isEnabled, presentationData: presentationData, updateText: updateText) + transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: width, height: itemHeight))) + contentHeight += itemHeight + hasPrevious = true + } + var removeKeys: [PeerInfoHeaderTextFieldNodeKey] = [] + for (key, _) in self.itemNodes { + if !fieldKeys.contains(key) { + removeKeys.append(key) + } + } + for key in removeKeys { + if let itemNode = self.itemNodes.removeValue(forKey: key) { + itemNode.removeFromSupernode() + } + } + + return contentHeight + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderMultiLineTextFieldNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderMultiLineTextFieldNode.swift new file mode 100644 index 00000000000..504cf031701 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderMultiLineTextFieldNode.swift @@ -0,0 +1,226 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import ContextUI +import TelegramPresentationData +import Display +import TelegramUIPreferences + +final class PeerInfoHeaderMultiLineTextFieldNode: ASDisplayNode, PeerInfoHeaderTextFieldNode, ASEditableTextNodeDelegate { + private let backgroundNode: ASDisplayNode + private let textNode: EditableTextNode + private let textNodeContainer: ASDisplayNode + private let measureTextNode: ImmediateTextNode + private let clearIconNode: ASImageNode + private let clearButtonNode: HighlightableButtonNode + private let topSeparator: ASDisplayNode + private let maskNode: ASImageNode + + private let requestUpdateHeight: () -> Void + + private var fontSize: PresentationFontSize? + private var theme: PresentationTheme? + private var currentParams: (width: CGFloat, safeInset: CGFloat)? + private var currentMeasuredHeight: CGFloat? + + var text: String { + return self.textNode.attributedText?.string ?? "" + } + + init(requestUpdateHeight: @escaping () -> Void) { + self.requestUpdateHeight = requestUpdateHeight + + self.backgroundNode = ASDisplayNode() + + self.textNode = EditableTextNode() + self.textNode.clipsToBounds = false + self.textNode.textView.clipsToBounds = false + self.textNode.textContainerInset = UIEdgeInsets() + + self.textNodeContainer = ASDisplayNode() + self.measureTextNode = ImmediateTextNode() + self.measureTextNode.maximumNumberOfLines = 0 + self.measureTextNode.isUserInteractionEnabled = false + self.measureTextNode.lineSpacing = 0.1 + self.topSeparator = ASDisplayNode() + + self.clearIconNode = ASImageNode() + self.clearIconNode.isLayerBacked = true + self.clearIconNode.displayWithoutProcessing = true + self.clearIconNode.displaysAsynchronously = false + self.clearIconNode.isHidden = true + + self.clearButtonNode = HighlightableButtonNode() + self.clearButtonNode.isHidden = true + self.clearButtonNode.isAccessibilityElement = false + + self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false + + super.init() + + self.addSubnode(self.backgroundNode) + self.textNodeContainer.addSubnode(self.textNode) + self.addSubnode(self.textNodeContainer) + self.addSubnode(self.clearIconNode) + self.addSubnode(self.clearButtonNode) + self.addSubnode(self.topSeparator) + self.addSubnode(self.maskNode) + + self.clearButtonNode.addTarget(self, action: #selector(self.clearButtonPressed), forControlEvents: .touchUpInside) + self.clearButtonNode.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + strongSelf.clearIconNode.layer.removeAnimation(forKey: "opacity") + strongSelf.clearIconNode.alpha = 0.4 + } else { + strongSelf.clearIconNode.alpha = 1.0 + strongSelf.clearIconNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + } + } + } + } + + @objc private func clearButtonPressed() { + guard let theme = self.theme else { + return + } + let font: UIFont + if let fontSize = self.fontSize { + font = Font.regular(fontSize.itemListBaseFontSize) + } else { + font = Font.regular(17.0) + } + let attributedText = NSAttributedString(string: "", font: font, textColor: theme.list.itemPrimaryTextColor) + self.textNode.attributedText = attributedText + self.requestUpdateHeight() + self.updateClearButtonVisibility() + } + + func update(width: CGFloat, safeInset: CGFloat, isSettings: Bool, hasPrevious: Bool, hasNext: Bool, placeholder: String, isEnabled: Bool, presentationData: PresentationData, updateText: String?) -> CGFloat { + self.currentParams = (width, safeInset) + + self.fontSize = presentationData.listsFontSize + let titleFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) + + if self.theme !== presentationData.theme { + self.theme = presentationData.theme + + self.backgroundNode.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor + + let textColor = presentationData.theme.list.itemPrimaryTextColor + self.textNode.typingAttributes = [NSAttributedString.Key.font.rawValue: titleFont, NSAttributedString.Key.foregroundColor.rawValue: textColor] + self.textNode.keyboardAppearance = presentationData.theme.rootController.keyboardColor.keyboardAppearance + self.textNode.tintColor = presentationData.theme.list.itemAccentColor + + self.textNode.clipsToBounds = true + self.textNode.delegate = self + self.textNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) + + self.clearIconNode.image = PresentationResourcesItemList.itemListClearInputIcon(presentationData.theme) + } + + self.topSeparator.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor + + let separatorX = safeInset + (hasPrevious ? 16.0 : 0.0) + self.topSeparator.frame = CGRect(origin: CGPoint(x: separatorX, y: 0.0), size: CGSize(width: width - separatorX - safeInset, height: UIScreenPixel)) + + let attributedPlaceholderText = NSAttributedString(string: placeholder, font: titleFont, textColor: presentationData.theme.list.itemPlaceholderTextColor) + if self.textNode.attributedPlaceholderText == nil || !self.textNode.attributedPlaceholderText!.isEqual(to: attributedPlaceholderText) { + self.textNode.attributedPlaceholderText = attributedPlaceholderText + } + + if let updateText = updateText { + let attributedText = NSAttributedString(string: updateText, font: titleFont, textColor: presentationData.theme.list.itemPrimaryTextColor) + self.textNode.attributedText = attributedText + } + + var measureText = self.textNode.attributedText?.string ?? "" + if measureText.hasSuffix("\n") || measureText.isEmpty { + measureText += "|" + } + let attributedMeasureText = NSAttributedString(string: measureText, font: titleFont, textColor: .gray) + self.measureTextNode.attributedText = attributedMeasureText + let measureTextSize = self.measureTextNode.updateLayout(CGSize(width: width - safeInset * 2.0 - 16.0 * 2.0 - 38.0, height: .greatestFiniteMagnitude)) + self.measureTextNode.frame = CGRect(origin: CGPoint(), size: measureTextSize) + self.currentMeasuredHeight = measureTextSize.height + + let height = measureTextSize.height + 22.0 + + let buttonSize = CGSize(width: 38.0, height: height) + self.clearButtonNode.frame = CGRect(origin: CGPoint(x: width - safeInset - buttonSize.width, y: 0.0), size: buttonSize) + if let image = self.clearIconNode.image { + self.clearIconNode.frame = CGRect(origin: CGPoint(x: width - safeInset - buttonSize.width + floor((buttonSize.width - image.size.width) / 2.0), y: floor((height - image.size.height) / 2.0)), size: image.size) + } + + let textNodeFrame = CGRect(origin: CGPoint(x: safeInset + 16.0, y: 10.0), size: CGSize(width: width - safeInset * 2.0 - 16.0 * 2.0 - 38.0, height: max(height, 1000.0))) + self.textNodeContainer.frame = textNodeFrame + self.textNode.frame = CGRect(origin: CGPoint(), size: textNodeFrame.size) + + self.backgroundNode.frame = CGRect(origin: CGPoint(x: safeInset, y: 0.0), size: CGSize(width: max(1.0, width - safeInset * 2.0), height: height)) + + let hasCorners = safeInset > 0.0 && (!hasPrevious || !hasNext) + let hasTopCorners = hasCorners && !hasPrevious + let hasBottomCorners = hasCorners && !hasNext + + self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + self.maskNode.frame = CGRect(origin: CGPoint(x: safeInset, y: 0.0), size: CGSize(width: width - safeInset - safeInset, height: height)) + + return height + } + + func editableTextNodeDidBeginEditing(_ editableTextNode: ASEditableTextNode) { + self.updateClearButtonVisibility() + } + + func editableTextNodeDidFinishEditing(_ editableTextNode: ASEditableTextNode) { + self.updateClearButtonVisibility() + } + + private func updateClearButtonVisibility() { + let isHidden = !self.textNode.isFirstResponder() || self.text.isEmpty + self.clearIconNode.isHidden = isHidden + self.clearButtonNode.isHidden = isHidden + self.clearButtonNode.isAccessibilityElement = isHidden + } + + func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { + guard let theme = self.theme else { + return true + } + let updatedText = (editableTextNode.textView.text as NSString).replacingCharacters(in: range, with: text) + if updatedText.count > 255 { + let attributedText = NSAttributedString(string: String(updatedText[updatedText.startIndex.. 0.1 { + self.requestUpdateHeight() + } + } + } + + func editableTextNodeShouldPaste(_ editableTextNode: ASEditableTextNode) -> Bool { + let text: String? = UIPasteboard.general.string + if let _ = text { + return true + } else { + return false + } + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift new file mode 100644 index 00000000000..a3a0913996d --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift @@ -0,0 +1,317 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import ContextUI +import TelegramPresentationData +import ManagedAnimationNode +import Display + +private enum MoreIconNodeState: Equatable { + case more + case search + case moreToSearch(Float) +} + +private final class MoreIconNode: ManagedAnimationNode { + private let duration: Double = 0.21 + private var iconState: MoreIconNodeState = .more + + init() { + super.init(size: CGSize(width: 30.0, height: 30.0)) + + self.trackTo(item: ManagedAnimationItem(source: .local("anim_moretosearch"), frames: .range(startFrame: 0, endFrame: 0), duration: 0.0)) + } + + func play() { + if case .more = self.iconState { + self.trackTo(item: ManagedAnimationItem(source: .local("anim_moredots"), frames: .range(startFrame: 0, endFrame: 46), duration: 0.76)) + } + } + + func enqueueState(_ state: MoreIconNodeState, animated: Bool) { + guard self.iconState != state else { + return + } + + let previousState = self.iconState + self.iconState = state + + let source = ManagedAnimationSource.local("anim_moretosearch") + + let totalLength: Int = 90 + if animated { + switch previousState { + case .more: + switch state { + case .more: + break + case .search: + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: 0, endFrame: totalLength), duration: self.duration)) + case let .moreToSearch(progress): + let frame = Int(progress * Float(totalLength)) + let duration = self.duration * Double(progress) + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: 0, endFrame: frame), duration: duration)) + } + case .search: + switch state { + case .more: + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: totalLength, endFrame: 0), duration: self.duration)) + case .search: + break + case let .moreToSearch(progress): + let frame = Int(progress * Float(totalLength)) + let duration = self.duration * Double((1.0 - progress)) + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: totalLength, endFrame: frame), duration: duration)) + } + case let .moreToSearch(currentProgress): + let currentFrame = Int(currentProgress * Float(totalLength)) + switch state { + case .more: + let duration = self.duration * Double(currentProgress) + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: currentFrame, endFrame: 0), duration: duration)) + case .search: + let duration = self.duration * (1.0 - Double(currentProgress)) + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: currentFrame, endFrame: totalLength), duration: duration)) + case let .moreToSearch(progress): + let frame = Int(progress * Float(totalLength)) + let duration = self.duration * Double(abs(currentProgress - progress)) + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: currentFrame, endFrame: frame), duration: duration)) + } + } + } else { + switch state { + case .more: + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: 0, endFrame: 0), duration: 0.0)) + case .search: + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: totalLength, endFrame: totalLength), duration: 0.0)) + case let .moreToSearch(progress): + let frame = Int(progress * Float(totalLength)) + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: frame, endFrame: frame), duration: 0.0)) + } + } + } +} + +final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { + let containerNode: ContextControllerSourceNode + let contextSourceNode: ContextReferenceContentNode + private let textNode: ImmediateTextNode + private let iconNode: ASImageNode + private var animationNode: MoreIconNode? + private let backgroundNode: NavigationBackgroundNode + + private var key: PeerInfoHeaderNavigationButtonKey? + + private var contentsColor: UIColor = .white + + var action: ((ASDisplayNode, ContextGesture?) -> Void)? + + init() { + self.contextSourceNode = ContextReferenceContentNode() + self.containerNode = ContextControllerSourceNode() + self.containerNode.animateScale = false + + self.textNode = ImmediateTextNode() + + self.iconNode = ASImageNode() + self.iconNode.displaysAsynchronously = false + self.iconNode.displayWithoutProcessing = true + + self.backgroundNode = NavigationBackgroundNode(color: .clear, enableBlur: true) + + super.init(pointerStyle: .insetRectangle(-8.0, 2.0)) + + self.isAccessibilityElement = true + self.accessibilityTraits = .button + + self.containerNode.addSubnode(self.contextSourceNode) + self.contextSourceNode.addSubnode(self.backgroundNode) + self.contextSourceNode.addSubnode(self.textNode) + self.contextSourceNode.addSubnode(self.iconNode) + + self.addSubnode(self.containerNode) + + self.containerNode.activated = { [weak self] gesture, _ in + guard let strongSelf = self else { + return + } + strongSelf.action?(strongSelf.contextSourceNode, gesture) + } + + self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside) + } + + @objc private func pressed() { + self.animationNode?.play() + self.action?(self.contextSourceNode, nil) + } + + func updateContentsColor(backgroundColor: UIColor, contentsColor: UIColor, transition: ContainedViewLayoutTransition) { + self.contentsColor = contentsColor + + self.backgroundNode.updateColor(color: backgroundColor, transition: transition) + + transition.updateTintColor(layer: self.textNode.layer, color: self.contentsColor) + transition.updateTintColor(layer: self.iconNode.layer, color: self.contentsColor) + + if let animationNode = self.animationNode { + transition.updateTintColor(layer: animationNode.imageNode.layer, color: self.contentsColor) + } + } + + func update(key: PeerInfoHeaderNavigationButtonKey, presentationData: PresentationData, height: CGFloat) -> CGSize { + let transition: ContainedViewLayoutTransition = .immediate + + var iconOffset = CGPoint() + switch key { + case .back: + iconOffset = CGPoint(x: -1.0, y: 0.0) + default: + break + } + + let textSize: CGSize + let isFirstTime = self.key == nil + if self.key != key { + self.key = key + + let text: String + var accessibilityText: String + var icon: UIImage? + var isBold = false + var isGestureEnabled = false + var isAnimation = false + var animationState: MoreIconNodeState = .more + switch key { + case .back: + text = "" + accessibilityText = presentationData.strings.Common_Back + icon = NavigationBar.thinBackArrowImage + case .edit: + text = presentationData.strings.Common_Edit + accessibilityText = text + case .cancel: + text = presentationData.strings.Common_Cancel + accessibilityText = text + isBold = false + case .done, .selectionDone: + text = presentationData.strings.Common_Done + accessibilityText = text + isBold = true + case .select: + text = presentationData.strings.Common_Select + accessibilityText = text + case .search: + text = "" + accessibilityText = presentationData.strings.Common_Search + icon = nil// PresentationResourcesRootController.navigationCompactSearchIcon(presentationData.theme) + isAnimation = true + animationState = .search + case .editPhoto: + text = presentationData.strings.Settings_EditPhoto + accessibilityText = text + case .editVideo: + text = presentationData.strings.Settings_EditVideo + accessibilityText = text + case .more: + text = "" + accessibilityText = presentationData.strings.Common_More + icon = nil// PresentationResourcesRootController.navigationMoreCircledIcon(presentationData.theme) + isGestureEnabled = true + isAnimation = true + animationState = .more + case .qrCode: + text = "" + accessibilityText = presentationData.strings.PeerInfo_QRCode_Title + icon = PresentationResourcesRootController.navigationQrCodeIcon(presentationData.theme) + case .moreToSearch: + text = "" + accessibilityText = "" + case .postStory: + text = "" + accessibilityText = presentationData.strings.Story_Privacy_PostStory + icon = PresentationResourcesRootController.navigationPostStoryIcon(presentationData.theme) + } + self.accessibilityLabel = accessibilityText + self.containerNode.isGestureEnabled = isGestureEnabled + + let font: UIFont = isBold ? Font.semibold(17.0) : Font.regular(17.0) + + self.textNode.attributedText = NSAttributedString(string: text, font: font, textColor: .white) + transition.updateTintColor(layer: self.textNode.layer, color: self.contentsColor) + self.iconNode.image = icon + transition.updateTintColor(layer: self.iconNode.layer, color: self.contentsColor) + + if isAnimation { + self.iconNode.isHidden = true + let animationNode: MoreIconNode + if let current = self.animationNode { + animationNode = current + } else { + animationNode = MoreIconNode() + self.animationNode = animationNode + self.contextSourceNode.addSubnode(animationNode) + animationNode.imageNode.layer.layerTintColor = self.contentsColor.cgColor + animationNode.customColor = .white + } + transition.updateTintColor(layer: animationNode.imageNode.layer, color: self.contentsColor) + animationNode.enqueueState(animationState, animated: !isFirstTime) + } else { + self.iconNode.isHidden = false + if let current = self.animationNode { + self.animationNode = nil + current.removeFromSupernode() + } + } + + textSize = self.textNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude)) + } else { + textSize = self.textNode.bounds.size + } + + let inset: CGFloat = 0.0 + + let resultSize: CGSize + + let textFrame = CGRect(origin: CGPoint(x: inset, y: floor((height - textSize.height) / 2.0)), size: textSize) + self.textNode.frame = textFrame + + if let animationNode = self.animationNode { + let animationSize = CGSize(width: 30.0, height: 30.0) + + animationNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((height - animationSize.height) / 2.0)), size: animationSize).offsetBy(dx: iconOffset.x, dy: iconOffset.y) + + let size = CGSize(width: animationSize.width + inset * 2.0, height: height) + self.containerNode.frame = CGRect(origin: CGPoint(), size: size) + self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: size) + resultSize = size + } else if let image = self.iconNode.image { + self.iconNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((height - image.size.height) / 2.0)), size: image.size).offsetBy(dx: iconOffset.x, dy: iconOffset.y) + + let size = CGSize(width: image.size.width + inset * 2.0, height: height) + self.containerNode.frame = CGRect(origin: CGPoint(), size: size) + self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: size) + resultSize = size + } else { + let size = CGSize(width: textSize.width + inset * 2.0, height: height) + self.containerNode.frame = CGRect(origin: CGPoint(), size: size) + self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: size) + resultSize = size + } + + let diameter: CGFloat = 32.0 + let backgroundWidth: CGFloat + if self.iconNode.image != nil || self.animationNode != nil { + backgroundWidth = diameter + } else { + backgroundWidth = max(diameter, resultSize.width + 12.0 * 2.0) + } + let backgroundFrame = CGRect(origin: CGPoint(x: floor((resultSize.width - backgroundWidth) * 0.5), y: floor((resultSize.height - diameter) * 0.5)), size: CGSize(width: backgroundWidth, height: diameter)) + transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) + self.backgroundNode.update(size: backgroundFrame.size, cornerRadius: diameter * 0.5, transition: transition) + + self.hitTestSlop = UIEdgeInsets(top: -2.0, left: -12.0, bottom: -2.0, right: -12.0) + + return resultSize + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift new file mode 100644 index 00000000000..c12dd1e3601 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift @@ -0,0 +1,277 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import ContextUI +import TelegramPresentationData +import Display + +enum PeerInfoHeaderNavigationButtonKey { + case back + case edit + case done + case cancel + case select + case selectionDone + case search + case editPhoto + case editVideo + case more + case qrCode + case moreToSearch + case postStory +} + +struct PeerInfoHeaderNavigationButtonSpec: Equatable { + let key: PeerInfoHeaderNavigationButtonKey + let isForExpandedView: Bool +} + +final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { + private var presentationData: PresentationData? + private(set) var leftButtonNodes: [PeerInfoHeaderNavigationButtonKey: PeerInfoHeaderNavigationButton] = [:] + private(set) var rightButtonNodes: [PeerInfoHeaderNavigationButtonKey: PeerInfoHeaderNavigationButton] = [:] + + private var currentLeftButtons: [PeerInfoHeaderNavigationButtonSpec] = [] + private var currentRightButtons: [PeerInfoHeaderNavigationButtonSpec] = [] + + private var backgroundContentColor: UIColor = .clear + private var contentsColor: UIColor = .white + + var performAction: ((PeerInfoHeaderNavigationButtonKey, ContextReferenceContentNode?, ContextGesture?) -> Void)? + + func updateContentsColor(backgroundContentColor: UIColor, contentsColor: UIColor, transition: ContainedViewLayoutTransition) { + self.backgroundContentColor = backgroundContentColor + self.contentsColor = contentsColor + + for (_, button) in self.leftButtonNodes { + button.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, transition: transition) + } + for (_, button) in self.rightButtonNodes { + button.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, transition: transition) + } + } + + func update(size: CGSize, presentationData: PresentationData, leftButtons: [PeerInfoHeaderNavigationButtonSpec], rightButtons: [PeerInfoHeaderNavigationButtonSpec], expandFraction: CGFloat, transition: ContainedViewLayoutTransition) { + let sideInset: CGFloat = 24.0 + + let maximumExpandOffset: CGFloat = 14.0 + let expandOffset: CGFloat = -expandFraction * maximumExpandOffset + + if self.currentLeftButtons != leftButtons || presentationData.strings !== self.presentationData?.strings { + self.currentLeftButtons = leftButtons + + var nextRegularButtonOrigin = sideInset + var nextExpandedButtonOrigin = sideInset + for spec in leftButtons.reversed() { + let buttonNode: PeerInfoHeaderNavigationButton + var wasAdded = false + if let current = self.leftButtonNodes[spec.key] { + buttonNode = current + } else { + wasAdded = true + buttonNode = PeerInfoHeaderNavigationButton() + self.leftButtonNodes[spec.key] = buttonNode + self.addSubnode(buttonNode) + buttonNode.action = { [weak self] _, gesture in + guard let strongSelf = self, let buttonNode = strongSelf.leftButtonNodes[spec.key] else { + return + } + strongSelf.performAction?(spec.key, buttonNode.contextSourceNode, gesture) + } + } + let buttonSize = buttonNode.update(key: spec.key, presentationData: presentationData, height: size.height) + var nextButtonOrigin = spec.isForExpandedView ? nextExpandedButtonOrigin : nextRegularButtonOrigin + + let buttonY: CGFloat + if case .back = spec.key { + buttonY = 0.0 + } else { + buttonY = expandOffset + (spec.isForExpandedView ? maximumExpandOffset : 0.0) + } + let buttonFrame = CGRect(origin: CGPoint(x: nextButtonOrigin, y: buttonY), size: buttonSize) + + nextButtonOrigin += buttonSize.width + 4.0 + if spec.isForExpandedView { + nextExpandedButtonOrigin = nextButtonOrigin + } else { + nextRegularButtonOrigin = nextButtonOrigin + } + let alphaFactor: CGFloat + if case .back = spec.key { + alphaFactor = 1.0 + } else { + alphaFactor = spec.isForExpandedView ? expandFraction : (1.0 - expandFraction) + } + if wasAdded { + buttonNode.frame = buttonFrame + buttonNode.alpha = 0.0 + transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) + buttonNode.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, transition: .immediate) + } else { + transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) + transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) + } + } + var removeKeys: [PeerInfoHeaderNavigationButtonKey] = [] + for (key, _) in self.leftButtonNodes { + if !leftButtons.contains(where: { $0.key == key }) { + removeKeys.append(key) + } + } + for key in removeKeys { + if let buttonNode = self.leftButtonNodes.removeValue(forKey: key) { + buttonNode.removeFromSupernode() + } + } + } else { + var nextRegularButtonOrigin = sideInset + var nextExpandedButtonOrigin = sideInset + for spec in leftButtons.reversed() { + if let buttonNode = self.leftButtonNodes[spec.key] { + let buttonSize = buttonNode.bounds.size + var nextButtonOrigin = spec.isForExpandedView ? nextExpandedButtonOrigin : nextRegularButtonOrigin + let buttonY: CGFloat + if case .back = spec.key { + buttonY = 0.0 + } else { + buttonY = expandOffset + (spec.isForExpandedView ? maximumExpandOffset : 0.0) + } + let buttonFrame = CGRect(origin: CGPoint(x: nextButtonOrigin, y: buttonY), size: buttonSize) + nextButtonOrigin += buttonSize.width + 4.0 + if spec.isForExpandedView { + nextExpandedButtonOrigin = nextButtonOrigin + } else { + nextRegularButtonOrigin = nextButtonOrigin + } + transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) + let alphaFactor: CGFloat + if case .back = spec.key { + alphaFactor = 1.0 + } else { + alphaFactor = spec.isForExpandedView ? expandFraction : (1.0 - expandFraction) + } + + var buttonTransition = transition + if case let .animated(duration, curve) = buttonTransition, alphaFactor == 0.0 { + buttonTransition = .animated(duration: duration * 0.25, curve: curve) + } + buttonTransition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) + } + } + } + + if self.currentRightButtons != rightButtons || presentationData.strings !== self.presentationData?.strings { + self.currentRightButtons = rightButtons + + var nextRegularButtonOrigin = size.width - sideInset + var nextExpandedButtonOrigin = size.width - sideInset + for spec in rightButtons.reversed() { + let buttonNode: PeerInfoHeaderNavigationButton + var wasAdded = false + + var key = spec.key + if key == .more || key == .search { + key = .moreToSearch + } + + if let current = self.rightButtonNodes[key] { + buttonNode = current + } else { + wasAdded = true + buttonNode = PeerInfoHeaderNavigationButton() + self.rightButtonNodes[key] = buttonNode + self.addSubnode(buttonNode) + } + buttonNode.action = { [weak self] _, gesture in + guard let strongSelf = self, let buttonNode = strongSelf.rightButtonNodes[key] else { + return + } + strongSelf.performAction?(spec.key, buttonNode.contextSourceNode, gesture) + } + let buttonSize = buttonNode.update(key: spec.key, presentationData: presentationData, height: size.height) + var nextButtonOrigin = spec.isForExpandedView ? nextExpandedButtonOrigin : nextRegularButtonOrigin + var buttonFrame = CGRect(origin: CGPoint(x: nextButtonOrigin - buttonSize.width, y: expandOffset + (spec.isForExpandedView ? maximumExpandOffset : 0.0)), size: buttonSize) + if case .postStory = spec.key { + buttonFrame.origin.x -= 12.0 + } + nextButtonOrigin -= buttonSize.width + 4.0 + if spec.isForExpandedView { + nextExpandedButtonOrigin = nextButtonOrigin + } else { + nextRegularButtonOrigin = nextButtonOrigin + } + let alphaFactor: CGFloat = spec.isForExpandedView ? expandFraction : (1.0 - expandFraction) + if wasAdded { + buttonNode.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, transition: .immediate) + + if key == .moreToSearch { + buttonNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2) + } + + buttonNode.frame = buttonFrame + buttonNode.alpha = 0.0 + transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) + } else { + transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) + transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) + } + } + var removeKeys: [PeerInfoHeaderNavigationButtonKey] = [] + for (key, _) in self.rightButtonNodes { + if key == .moreToSearch { + if !rightButtons.contains(where: { $0.key == .more || $0.key == .search }) { + removeKeys.append(key) + } + } else if !rightButtons.contains(where: { $0.key == key }) { + removeKeys.append(key) + } + } + for key in removeKeys { + if let buttonNode = self.rightButtonNodes.removeValue(forKey: key) { + if key == .moreToSearch { + buttonNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak buttonNode] _ in + buttonNode?.removeFromSupernode() + }) + buttonNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false) + } else { + buttonNode.removeFromSupernode() + } + } + } + } else { + var nextRegularButtonOrigin = size.width - sideInset + var nextExpandedButtonOrigin = size.width - sideInset + + for spec in rightButtons.reversed() { + var key = spec.key + if key == .more || key == .search { + key = .moreToSearch + } + + if let buttonNode = self.rightButtonNodes[key] { + let buttonSize = buttonNode.bounds.size + var nextButtonOrigin = spec.isForExpandedView ? nextExpandedButtonOrigin : nextRegularButtonOrigin + var buttonFrame = CGRect(origin: CGPoint(x: nextButtonOrigin - buttonSize.width, y: expandOffset + (spec.isForExpandedView ? maximumExpandOffset : 0.0)), size: buttonSize) + if case .postStory = spec.key { + buttonFrame.origin.x -= 12.0 + } + nextButtonOrigin -= buttonSize.width + 4.0 + if spec.isForExpandedView { + nextExpandedButtonOrigin = nextButtonOrigin + } else { + nextRegularButtonOrigin = nextButtonOrigin + } + transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) + let alphaFactor: CGFloat = spec.isForExpandedView ? expandFraction : (1.0 - expandFraction) + + var buttonTransition = transition + if case let .animated(duration, curve) = buttonTransition, alphaFactor == 0.0 { + buttonTransition = .animated(duration: duration * 0.25, curve: curve) + } + buttonTransition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) + } + } + } + self.presentationData = presentationData + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift new file mode 100644 index 00000000000..2483a8bd414 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -0,0 +1,1943 @@ +// MARK: Nicegram HidePhone +import NGData +// +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import Postbox +import TelegramCore +import AvatarNode +import AccountContext +import SwiftSignalKit +import TelegramPresentationData +import PhotoResources +import PeerAvatarGalleryUI +import TelegramStringFormatting +import PhoneNumberFormat +import ActivityIndicator +import TelegramUniversalVideoContent +import GalleryUI +import UniversalMediaPlayer +import RadialStatusNode +import TelegramUIPreferences +import PeerInfoAvatarListNode +import AnimationUI +import ContextUI +import ManagedAnimationNode +import ComponentFlow +import EmojiStatusComponent +import AnimationCache +import MultiAnimationRenderer +import ComponentDisplayAdapters +import ChatTitleView +import AppBundle +import AvatarVideoNode +import PeerInfoVisualMediaPaneNode +import AvatarStoryIndicatorComponent +import ComponentDisplayAdapters +import ChatAvatarNavigationNode +import MultiScaleTextNode +import PeerInfoCoverComponent + +final class PeerInfoHeaderNavigationTransition { + let sourceNavigationBar: NavigationBar + let sourceTitleView: ChatTitleView + let sourceTitleFrame: CGRect + let sourceSubtitleFrame: CGRect + let previousAvatarView: UIView? + let fraction: CGFloat + + init(sourceNavigationBar: NavigationBar, sourceTitleView: ChatTitleView, sourceTitleFrame: CGRect, sourceSubtitleFrame: CGRect, previousAvatarView: UIView?, fraction: CGFloat) { + self.sourceNavigationBar = sourceNavigationBar + self.sourceTitleView = sourceTitleView + self.sourceTitleFrame = sourceTitleFrame + self.sourceSubtitleFrame = sourceSubtitleFrame + self.previousAvatarView = previousAvatarView + self.fraction = fraction + } +} + +final class PeerInfoHeaderRegularContentNode: ASDisplayNode { +} + +enum PeerInfoHeaderTextFieldNodeKey: Equatable { + case firstName + case lastName + case title + case description +} + +protocol PeerInfoHeaderTextFieldNode: ASDisplayNode { + var text: String { get } + + func update(width: CGFloat, safeInset: CGFloat, isSettings: Bool, hasPrevious: Bool, hasNext: Bool, placeholder: String, isEnabled: Bool, presentationData: PresentationData, updateText: String?) -> CGFloat +} + +private let TitleNodeStateRegular = 0 +private let TitleNodeStateExpanded = 1 + +final class PeerInfoHeaderNode: ASDisplayNode { + private var context: AccountContext + private weak var controller: PeerInfoScreenImpl? + private var presentationData: PresentationData? + private var state: PeerInfoState? + private var peer: Peer? + private var threadData: MessageHistoryThreadData? + private var avatarSize: CGFloat? + + private let isOpenedFromChat: Bool + private let isSettings: Bool + private let videoCallsEnabled: Bool + private let forumTopicThreadId: Int64? + private let chatLocation: ChatLocation + + private(set) var isAvatarExpanded: Bool + var skipCollapseCompletion = false + var ignoreCollapse = false + + let avatarClippingNode: SparseNode + let avatarListNode: PeerInfoAvatarListNode + + let backgroundBannerView: UIView + let backgroundCover = ComponentView() + let buttonsContainerNode: SparseNode + let regularContentNode: PeerInfoHeaderRegularContentNode + let editingContentNode: PeerInfoHeaderEditingContentNode + let avatarOverlayNode: PeerInfoEditingAvatarOverlayNode + let titleNodeContainer: ASDisplayNode + let titleNodeRawContainer: ASDisplayNode + let titleNode: MultiScaleTextNode + let titleCredibilityIconView: ComponentHostView + var credibilityIconSize: CGSize? + let titleExpandedCredibilityIconView: ComponentHostView + var titleExpandedCredibilityIconSize: CGSize? + let subtitleNodeContainer: ASDisplayNode + let subtitleNodeRawContainer: ASDisplayNode + let subtitleNode: MultiScaleTextNode + var subtitleBackgroundNode: ASDisplayNode? + var subtitleBackgroundButton: HighlightTrackingButtonNode? + var subtitleArrowNode: ASImageNode? + let panelSubtitleNode: MultiScaleTextNode + let usernameNodeContainer: ASDisplayNode + let usernameNodeRawContainer: ASDisplayNode + let usernameNode: MultiScaleTextNode + var actionButtonNodes: [PeerInfoHeaderButtonKey: PeerInfoHeaderActionButtonNode] = [:] + var buttonNodes: [PeerInfoHeaderButtonKey: PeerInfoHeaderButtonNode] = [:] + let backgroundNode: NavigationBackgroundNode + let expandedBackgroundNode: NavigationBackgroundNode + let separatorNode: ASDisplayNode + let navigationBackgroundNode: ASDisplayNode + let navigationBackgroundBackgroundNode: ASDisplayNode + var navigationTitle: String? + let navigationTitleNode: ImmediateTextNode + let navigationSeparatorNode: ASDisplayNode + let navigationButtonContainer: PeerInfoHeaderNavigationButtonContainerNode + + var performButtonAction: ((PeerInfoHeaderButtonKey, ContextGesture?) -> Void)? + var requestAvatarExpansion: ((Bool, [AvatarGalleryEntry], AvatarGalleryEntry?, (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?) -> Void)? + var requestOpenAvatarForEditing: ((Bool) -> Void)? + var cancelUpload: (() -> Void)? + var requestUpdateLayout: ((Bool) -> Void)? + var animateOverlaysFadeIn: (() -> Void)? + + var displayAvatarContextMenu: ((ASDisplayNode, ContextGesture?) -> Void)? + var displayCopyContextMenu: ((ASDisplayNode, Bool, Bool) -> Void)? + var displayEmojiPackTooltip: (() -> Void)? + + var displayPremiumIntro: ((UIView, PeerEmojiStatus?, Signal<(TelegramMediaFile, LoadedStickerPack)?, NoError>, Bool) -> Void)? + + var navigateToForum: (() -> Void)? + + var navigationTransition: PeerInfoHeaderNavigationTransition? + + var backgroundAlpha: CGFloat = 1.0 + var updateHeaderAlpha: ((CGFloat, ContainedViewLayoutTransition) -> Void)? + + let animationCache: AnimationCache + let animationRenderer: MultiAnimationRenderer + + var emojiStatusPackDisposable = MetaDisposable() + var emojiStatusFileAndPackTitle = Promise<(TelegramMediaFile, LoadedStickerPack)?>() + + private var validLayout: (width: CGFloat, deviceMetrics: DeviceMetrics)? + + init(context: AccountContext, controller: PeerInfoScreenImpl, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, isMediaOnly: Bool, isSettings: Bool, forumTopicThreadId: Int64?, chatLocation: ChatLocation) { + self.context = context + self.controller = controller + self.isAvatarExpanded = avatarInitiallyExpanded + self.isOpenedFromChat = isOpenedFromChat + self.isSettings = isSettings + self.videoCallsEnabled = true + self.forumTopicThreadId = forumTopicThreadId + self.chatLocation = chatLocation + + self.avatarClippingNode = SparseNode() + self.avatarClippingNode.alpha = 0.996 + self.avatarClippingNode.clipsToBounds = true + + self.avatarListNode = PeerInfoAvatarListNode(context: context, readyWhenGalleryLoads: avatarInitiallyExpanded, isSettings: isSettings) + + self.titleNodeContainer = ASDisplayNode() + self.titleNodeRawContainer = ASDisplayNode() + self.titleNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded]) + self.titleNode.displaysAsynchronously = false + + self.titleCredibilityIconView = ComponentHostView() + self.titleNode.stateNode(forKey: TitleNodeStateRegular)?.view.addSubview(self.titleCredibilityIconView) + + self.titleExpandedCredibilityIconView = ComponentHostView() + self.titleNode.stateNode(forKey: TitleNodeStateExpanded)?.view.addSubview(self.titleExpandedCredibilityIconView) + + self.subtitleNodeContainer = ASDisplayNode() + self.subtitleNodeRawContainer = ASDisplayNode() + self.subtitleNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded]) + self.subtitleNode.displaysAsynchronously = false + + self.panelSubtitleNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded]) + self.panelSubtitleNode.displaysAsynchronously = false + + self.usernameNodeContainer = ASDisplayNode() + self.usernameNodeRawContainer = ASDisplayNode() + self.usernameNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded]) + self.usernameNode.displaysAsynchronously = false + + self.backgroundBannerView = UIView() + self.backgroundBannerView.clipsToBounds = true + self.backgroundBannerView.isUserInteractionEnabled = false + + self.buttonsContainerNode = SparseNode() + self.buttonsContainerNode.clipsToBounds = true + + self.regularContentNode = PeerInfoHeaderRegularContentNode() + var requestUpdateLayoutImpl: (() -> Void)? + self.editingContentNode = PeerInfoHeaderEditingContentNode(context: context, requestUpdateLayout: { + requestUpdateLayoutImpl?() + }) + self.editingContentNode.alpha = 0.0 + + self.avatarOverlayNode = PeerInfoEditingAvatarOverlayNode(context: context) + self.avatarOverlayNode.isUserInteractionEnabled = false + + self.navigationBackgroundNode = ASDisplayNode() + self.navigationBackgroundNode.isHidden = true + self.navigationBackgroundNode.isUserInteractionEnabled = false + + self.navigationBackgroundBackgroundNode = ASDisplayNode() + self.navigationBackgroundBackgroundNode.isUserInteractionEnabled = false + + self.navigationTitleNode = ImmediateTextNode() + + self.navigationSeparatorNode = ASDisplayNode() + + self.navigationButtonContainer = PeerInfoHeaderNavigationButtonContainerNode() + + self.backgroundNode = NavigationBackgroundNode(color: .clear) + self.backgroundNode.isHidden = true + self.backgroundNode.isUserInteractionEnabled = false + self.expandedBackgroundNode = NavigationBackgroundNode(color: .clear) + self.expandedBackgroundNode.isHidden = false + self.expandedBackgroundNode.isUserInteractionEnabled = false + + self.separatorNode = ASDisplayNode() + self.separatorNode.isLayerBacked = true + + self.animationCache = context.animationCache + self.animationRenderer = context.animationRenderer + + super.init() + + requestUpdateLayoutImpl = { [weak self] in + self?.requestUpdateLayout?(false) + } + + self.addSubnode(self.backgroundNode) + self.addSubnode(self.expandedBackgroundNode) + self.view.addSubview(self.backgroundBannerView) + self.titleNodeContainer.addSubnode(self.titleNode) + self.subtitleNodeContainer.addSubnode(self.subtitleNode) + self.subtitleNodeContainer.addSubnode(self.panelSubtitleNode) + self.usernameNodeContainer.addSubnode(self.usernameNode) + + self.regularContentNode.addSubnode(self.avatarClippingNode) + self.avatarClippingNode.addSubnode(self.avatarListNode) + + self.regularContentNode.addSubnode(self.avatarListNode.listContainerNode.controlsClippingOffsetNode) + self.regularContentNode.addSubnode(self.titleNodeContainer) + self.regularContentNode.addSubnode(self.subtitleNodeContainer) + self.regularContentNode.addSubnode(self.subtitleNodeRawContainer) + self.regularContentNode.addSubnode(self.usernameNodeContainer) + self.regularContentNode.addSubnode(self.usernameNodeRawContainer) + + self.addSubnode(self.regularContentNode) + + if !isMediaOnly { + self.regularContentNode.addSubnode(self.buttonsContainerNode) + } + + self.addSubnode(self.editingContentNode) + self.addSubnode(self.avatarOverlayNode) + self.addSubnode(self.navigationBackgroundNode) + self.navigationBackgroundNode.addSubnode(self.navigationBackgroundBackgroundNode) + self.navigationBackgroundNode.addSubnode(self.navigationTitleNode) + self.navigationBackgroundNode.addSubnode(self.navigationSeparatorNode) + self.addSubnode(self.navigationButtonContainer) + self.addSubnode(self.separatorNode) + + self.avatarListNode.avatarContainerNode.tapped = { [weak self] in + self?.initiateAvatarExpansion(gallery: false, first: false) + } + self.avatarListNode.avatarContainerNode.contextAction = { [weak self] node, gesture in + self?.displayAvatarContextMenu?(node, gesture) + } + self.avatarListNode.avatarContainerNode.emojiTapped = { [weak self] in + self?.displayEmojiPackTooltip?() + } + + self.editingContentNode.avatarNode.tapped = { [weak self] confirm in + self?.initiateAvatarExpansion(gallery: true, first: true) + } + self.editingContentNode.requestEditing = { [weak self] in + self?.requestOpenAvatarForEditing?(true) + } + + self.avatarListNode.itemsUpdated = { [weak self] items in + guard let strongSelf = self, let state = strongSelf.state, let peer = strongSelf.peer, let presentationData = strongSelf.presentationData, let avatarSize = strongSelf.avatarSize else { + return + } + strongSelf.editingContentNode.avatarNode.update(peer: peer, threadData: strongSelf.threadData, chatLocation: chatLocation, item: strongSelf.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing) + } + + self.avatarListNode.animateOverlaysFadeIn = { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.navigationButtonContainer.layer.animateAlpha(from: 0.0, to: strongSelf.navigationButtonContainer.alpha, duration: 0.25) + strongSelf.avatarListNode.listContainerNode.topShadowNode.layer.animateAlpha(from: 0.0, to: strongSelf.avatarListNode.listContainerNode.topShadowNode.alpha, duration: 0.25) + + strongSelf.avatarListNode.listContainerNode.bottomShadowNode.alpha = 1.0 + strongSelf.avatarListNode.listContainerNode.bottomShadowNode.layer.animateAlpha(from: 0.0, to: strongSelf.avatarListNode.listContainerNode.bottomShadowNode.alpha, duration: 0.25) + strongSelf.avatarListNode.listContainerNode.controlsContainerNode.layer.animateAlpha(from: 0.0, to: strongSelf.avatarListNode.listContainerNode.controlsContainerNode.alpha, duration: 0.25) + + strongSelf.titleNode.layer.animateAlpha(from: 0.0, to: strongSelf.titleNode.alpha, duration: 0.25) + strongSelf.subtitleNode.layer.animateAlpha(from: 0.0, to: strongSelf.subtitleNode.alpha, duration: 0.25) + + strongSelf.animateOverlaysFadeIn?() + } + } + + deinit { + self.emojiStatusPackDisposable.dispose() + } + + override func didLoad() { + super.didLoad() + + let usernameGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.handleUsernameLongPress(_:))) + self.usernameNodeRawContainer.view.addGestureRecognizer(usernameGestureRecognizer) + + let phoneGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.handlePhoneLongPress(_:))) + self.subtitleNodeRawContainer.view.addGestureRecognizer(phoneGestureRecognizer) + } + + @objc private func handleUsernameLongPress(_ gestureRecognizer: UILongPressGestureRecognizer) { + if gestureRecognizer.state == .began { + self.displayCopyContextMenu?(self.usernameNodeRawContainer, !self.isAvatarExpanded, true) + } + } + + @objc private func handlePhoneLongPress(_ gestureRecognizer: UILongPressGestureRecognizer) { + if gestureRecognizer.state == .began { + self.displayCopyContextMenu?(self.subtitleNodeRawContainer, true, !self.isAvatarExpanded) + } + } + + @objc private func subtitleBackgroundPressed() { + self.navigateToForum?() + } + + func invokeDisplayPremiumIntro() { + self.displayPremiumIntro?(self.isAvatarExpanded ? self.titleExpandedCredibilityIconView : self.titleCredibilityIconView, nil, .never(), self.isAvatarExpanded) + } + + func initiateAvatarExpansion(gallery: Bool, first: Bool) { + if let peer = self.peer, peer.profileImageRepresentations.isEmpty && gallery { + self.requestOpenAvatarForEditing?(false) + return + } + if self.isAvatarExpanded || gallery { + if let currentEntry = self.avatarListNode.listContainerNode.currentEntry, let firstEntry = self.avatarListNode.listContainerNode.galleryEntries.first { + let entry = first ? firstEntry : currentEntry + self.requestAvatarExpansion?(true, self.avatarListNode.listContainerNode.galleryEntries, entry, self.avatarTransitionArguments(entry: currentEntry)) + } + } else if let entry = self.avatarListNode.listContainerNode.galleryEntries.first { + self.requestAvatarExpansion?(false, self.avatarListNode.listContainerNode.galleryEntries, nil, self.avatarTransitionArguments(entry: entry)) + } else if let storyParams = self.avatarListNode.listContainerNode.storyParams, storyParams.count != 0 { + self.requestAvatarExpansion?(false, self.avatarListNode.listContainerNode.galleryEntries, nil, nil) + } else { + self.cancelUpload?() + } + } + + func avatarTransitionArguments(entry: AvatarGalleryEntry) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? { + if self.isAvatarExpanded { + if let avatarNode = self.avatarListNode.listContainerNode.currentItemNode?.imageNode { + return (avatarNode, avatarNode.bounds, { [weak avatarNode] in + return (avatarNode?.view.snapshotContentTree(unhide: true), nil) + }) + } else { + return nil + } + } else if entry == self.avatarListNode.listContainerNode.galleryEntries.first { + let avatarNode = self.avatarListNode.avatarContainerNode.avatarNode + return (avatarNode, avatarNode.bounds, { [weak avatarNode] in + return (avatarNode?.view.snapshotContentTree(unhide: true), nil) + }) + } else { + return nil + } + } + + func addToAvatarTransitionSurface(view: UIView) { + if self.isAvatarExpanded { + self.avatarListNode.listContainerNode.view.addSubview(view) + } else { + self.view.addSubview(view) + } + } + + func updateAvatarIsHidden(entry: AvatarGalleryEntry?) { + if let entry = entry { + self.avatarListNode.avatarContainerNode.containerNode.isHidden = entry == self.avatarListNode.listContainerNode.galleryEntries.first + self.editingContentNode.avatarNode.isHidden = entry == self.avatarListNode.listContainerNode.galleryEntries.first + } else { + self.avatarListNode.avatarContainerNode.containerNode.isHidden = false + self.editingContentNode.avatarNode.isHidden = false + } + self.avatarListNode.listContainerNode.updateEntryIsHidden(entry: entry) + } + + private enum CredibilityIcon: Equatable { + case none + case premium + case verified + case fake + case scam + case emojiStatus(PeerEmojiStatus) + } + + private var currentCredibilityIcon: 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, metrics: LayoutMetrics, deviceMetrics: DeviceMetrics, transition: ContainedViewLayoutTransition, additive: Bool, animateHeader: Bool) -> CGFloat { + self.state = state + self.peer = peer + self.threadData = threadData + self.avatarListNode.listContainerNode.peer = peer.flatMap(EnginePeer.init) + + let isFirstTime = self.validLayout == nil + self.validLayout = (width, deviceMetrics) + + let previousPanelStatusData = self.currentPanelStatusData + self.currentPanelStatusData = panelStatusData.0 + + let avatarSize: CGFloat = isModalOverlay ? 200.0 : 100.0 + self.avatarSize = avatarSize + + var contentOffset = contentOffset + + if isMediaOnly { + if isModalOverlay { + contentOffset = 312.0 + } else { + contentOffset = 212.0 + } + } + + let isLandscape = containerInset > 16.0 + + let themeUpdated = self.presentationData?.theme !== presentationData.theme + self.presentationData = presentationData + + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 }) + let credibilityIcon: CredibilityIcon + if let peer = peer { + if peer.isFake { + credibilityIcon = .fake + } else if peer.isScam { + credibilityIcon = .scam + } else if let user = peer as? TelegramUser, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled { + credibilityIcon = .emojiStatus(emojiStatus) + } else if peer.isVerified { + credibilityIcon = .verified + } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled && (peer.id != self.context.account.peerId || self.isSettings) { + credibilityIcon = .premium + } else { + credibilityIcon = .none + } + } else { + credibilityIcon = .none + } + + var isForum = false + if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) { + isForum = true + } + + self.regularContentNode.alpha = state.isEditing ? 0.0 : 1.0 + self.editingContentNode.alpha = state.isEditing ? 1.0 : 0.0 + + let editingContentHeight = self.editingContentNode.update(width: width, safeInset: containerInset, statusBarHeight: statusBarHeight, navigationHeight: navigationHeight, isModalOverlay: isModalOverlay, peer: state.isEditing ? peer : nil, threadData: threadData, chatLocation: self.chatLocation, cachedData: cachedData, isContact: isContact, isSettings: isSettings, presentationData: presentationData, transition: transition) + transition.updateFrame(node: self.editingContentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -contentOffset), size: CGSize(width: width, height: editingContentHeight))) + + let avatarOverlayFarme = self.editingContentNode.convert(self.editingContentNode.avatarNode.frame, to: self) + transition.updateFrame(node: self.avatarOverlayNode, frame: avatarOverlayFarme) + + var transitionSourceHeight: CGFloat = 0.0 + var transitionFraction: CGFloat = 0.0 + var transitionSourceAvatarFrame: CGRect? + var transitionSourceTitleFrame = CGRect() + var transitionSourceSubtitleFrame = CGRect() + + let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 22.0), size: CGSize(width: avatarSize, height: avatarSize)) + + self.backgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) + + let headerBackgroundColor: UIColor = presentationData.theme.list.blocksBackgroundColor + + let regularNavigationContentsAccentColor: UIColor = peer?.profileColor != nil ? .white : presentationData.theme.list.itemAccentColor + let collapsedHeaderNavigationContentsAccentColor = presentationData.theme.list.itemAccentColor + let expandedAvatarNavigationContentsAccentColor: UIColor = .white + + let regularNavigationContentsPrimaryColor: UIColor = peer?.profileColor != nil ? .white : presentationData.theme.list.itemPrimaryTextColor + let collapsedHeaderNavigationContentsPrimaryColor = presentationData.theme.list.itemPrimaryTextColor + let expandedAvatarNavigationContentsPrimaryColor: UIColor = .white + + let regularContentButtonBackgroundColor: UIColor + let collapsedHeaderContentButtonBackgroundColor = presentationData.theme.list.itemBlocksBackgroundColor + let expandedAvatarContentButtonBackgroundColor: UIColor = UIColor(white: 0.0, alpha: 0.1) + + let regularHeaderButtonBackgroundColor: UIColor + let collapsedHeaderButtonBackgroundColor: UIColor = .clear + let expandedAvatarHeaderButtonBackgroundColor: UIColor = UIColor(white: 0.0, alpha: 0.1) + + let regularContentButtonForegroundColor: UIColor = peer?.profileColor != nil ? UIColor.white : presentationData.theme.list.itemAccentColor + let collapsedHeaderContentButtonForegroundColor = presentationData.theme.list.itemAccentColor + let expandedAvatarContentButtonForegroundColor: UIColor = .white + + let regularNavigationContentsSecondaryColor: UIColor + if let profileColor = peer?.profileColor { + let backgroundColors = self.context.peerNameColors.getProfile(profileColor, dark: presentationData.theme.overallDarkAppearance) + regularNavigationContentsSecondaryColor = UIColor(white: 1.0, alpha: 0.6).blitOver(backgroundColors.main.withMultiplied(hue: 1.0, saturation: 2.2, brightness: 1.5), alpha: 1.0) + + let baseButtonBackgroundColor: UIColor + if presentationData.theme.overallDarkAppearance { + baseButtonBackgroundColor = UIColor(white: 0.0, alpha: 0.25) + } else { + baseButtonBackgroundColor = UIColor(white: 1.0, alpha: 0.25) + } + regularContentButtonBackgroundColor = baseButtonBackgroundColor.blendOver(background: backgroundColors.main) + regularHeaderButtonBackgroundColor = baseButtonBackgroundColor.blendOver(background: (backgroundColors.secondary ?? backgroundColors.main).mixedWith(backgroundColors.main, alpha: 0.1)) + } else { + regularNavigationContentsSecondaryColor = presentationData.theme.list.itemSecondaryTextColor + regularContentButtonBackgroundColor = presentationData.theme.list.itemBlocksBackgroundColor + regularHeaderButtonBackgroundColor = .clear + } + let collapsedHeaderNavigationContentsSecondaryColor = presentationData.theme.list.itemSecondaryTextColor + let expandedAvatarNavigationContentsSecondaryColor: UIColor = .white + + let navigationContentsAccentColor: UIColor + let navigationContentsPrimaryColor: UIColor + let navigationContentsSecondaryColor: UIColor + + let contentButtonBackgroundColor: UIColor + let contentButtonForegroundColor: UIColor + + let headerButtonBackgroundColor: UIColor + + var panelWithAvatarHeight: CGFloat = 35.0 + avatarSize + if threadData != nil { + panelWithAvatarHeight += 10.0 + } + + let innerBackgroundTransitionFraction: CGFloat + + let navigationTransition: ContainedViewLayoutTransition + if transition.isAnimated { + navigationTransition = transition + } else { + navigationTransition = animateHeader ? .animated(duration: 0.2, curve: .easeInOut) : .immediate + } + + let backgroundBannerAlpha: CGFloat + + var effectiveSeparatorAlpha: CGFloat + if let navigationTransition = self.navigationTransition { + transitionSourceHeight = navigationTransition.sourceNavigationBar.backgroundNode.bounds.height + transitionFraction = navigationTransition.fraction + + innerBackgroundTransitionFraction = 0.0 + backgroundBannerAlpha = 1.0 + + if let avatarNavigationNode = navigationTransition.sourceNavigationBar.rightButtonNode.singleCustomNode as? ChatAvatarNavigationNode { + if let statusView = avatarNavigationNode.statusView.view { + transitionSourceAvatarFrame = statusView.convert(statusView.bounds, to: navigationTransition.sourceNavigationBar.view) + } else { + transitionSourceAvatarFrame = avatarNavigationNode.avatarNode.view.convert(avatarNavigationNode.avatarNode.view.bounds, to: navigationTransition.sourceNavigationBar.view) + } + transition.updateAlpha(node: self.avatarListNode.avatarContainerNode.avatarNode, alpha: 1.0 - transitionFraction) + } else { + if deviceMetrics.hasDynamicIsland && !isLandscape { + transitionSourceAvatarFrame = CGRect(origin: CGPoint(x: avatarFrame.minX, y: -20.0), size: avatarFrame.size).insetBy(dx: avatarSize * 0.4, dy: avatarSize * 0.4) + } else { + transitionSourceAvatarFrame = avatarFrame.offsetBy(dx: 0.0, dy: -avatarFrame.maxY).insetBy(dx: avatarSize * 0.4, dy: avatarSize * 0.4) + } + } + transitionSourceTitleFrame = navigationTransition.sourceTitleFrame + transitionSourceSubtitleFrame = navigationTransition.sourceSubtitleFrame + + transition.updateAlpha(layer: self.backgroundBannerView.layer, alpha: 1.0 - transitionFraction) + + self.expandedBackgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor.mixedWith(headerBackgroundColor, alpha: 1.0 - transitionFraction), forceKeepBlur: true, transition: transition) + effectiveSeparatorAlpha = transitionFraction + + if self.isAvatarExpanded, case .animated = transition, transitionFraction == 1.0 { + self.avatarListNode.animateAvatarCollapse(transition: transition) + } + self.avatarClippingNode.clipsToBounds = false + } else { + let backgroundTransitionStepDistance: CGFloat = 50.0 + var backgroundTransitionDistance: CGFloat = navigationHeight + panelWithAvatarHeight - backgroundTransitionStepDistance + if self.isSettings { + backgroundTransitionDistance -= 100.0 + } + let contentOffset = max(0.0, contentOffset - backgroundTransitionDistance) + innerBackgroundTransitionFraction = max(0.0, min(1.0, contentOffset / backgroundTransitionStepDistance)) + + self.expandedBackgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.opaqueBackgroundColor.mixedWith(headerBackgroundColor, alpha: 1.0 - innerBackgroundTransitionFraction), forceKeepBlur: true, transition: transition) + + if state.isEditing { + backgroundBannerAlpha = 0.0 + } else { + if 1.0 - innerBackgroundTransitionFraction < 0.5 { + backgroundBannerAlpha = 0.0 + } else { + backgroundBannerAlpha = 1.0 + } + } + navigationTransition.updateAlpha(layer: self.backgroundBannerView.layer, alpha: backgroundBannerAlpha) + + effectiveSeparatorAlpha = innerBackgroundTransitionFraction + + self.avatarClippingNode.clipsToBounds = true + } + + if state.isEditing { + navigationContentsAccentColor = collapsedHeaderNavigationContentsAccentColor + navigationContentsPrimaryColor = collapsedHeaderNavigationContentsPrimaryColor + navigationContentsSecondaryColor = collapsedHeaderNavigationContentsSecondaryColor + contentButtonBackgroundColor = collapsedHeaderContentButtonBackgroundColor + contentButtonForegroundColor = collapsedHeaderContentButtonForegroundColor + + headerButtonBackgroundColor = collapsedHeaderButtonBackgroundColor + } else if self.isAvatarExpanded { + navigationContentsAccentColor = expandedAvatarNavigationContentsAccentColor + navigationContentsPrimaryColor = expandedAvatarNavigationContentsPrimaryColor + navigationContentsSecondaryColor = expandedAvatarNavigationContentsSecondaryColor + contentButtonBackgroundColor = expandedAvatarContentButtonBackgroundColor + contentButtonForegroundColor = expandedAvatarContentButtonForegroundColor + + headerButtonBackgroundColor = expandedAvatarHeaderButtonBackgroundColor + } else { + let effectiveTransitionFraction: CGFloat = innerBackgroundTransitionFraction < 0.5 ? 0.0 : 1.0 + + navigationContentsAccentColor = regularNavigationContentsAccentColor.mixedWith(collapsedHeaderNavigationContentsAccentColor, alpha: effectiveTransitionFraction) + navigationContentsPrimaryColor = regularNavigationContentsPrimaryColor.mixedWith(collapsedHeaderNavigationContentsPrimaryColor, alpha: effectiveTransitionFraction) + navigationContentsSecondaryColor = regularNavigationContentsSecondaryColor.mixedWith(collapsedHeaderNavigationContentsSecondaryColor, alpha: effectiveTransitionFraction) + + contentButtonBackgroundColor = regularContentButtonBackgroundColor//.mixedWith(collapsedHeaderContentButtonBackgroundColor, alpha: effectiveTransitionFraction) + contentButtonForegroundColor = regularContentButtonForegroundColor//.mixedWith(collapsedHeaderContentButtonForegroundColor, alpha: effectiveTransitionFraction) + + headerButtonBackgroundColor = regularHeaderButtonBackgroundColor.mixedWith(collapsedHeaderButtonBackgroundColor, alpha: effectiveTransitionFraction) + } + + do { + self.currentCredibilityIcon = credibilityIcon + + var currentEmojiStatus: PeerEmojiStatus? + let emojiRegularStatusContent: EmojiStatusComponent.Content + let emojiExpandedStatusContent: EmojiStatusComponent.Content + switch credibilityIcon { + case .none: + emojiRegularStatusContent = .none + emojiExpandedStatusContent = .none + case .premium: + emojiRegularStatusContent = .premium(color: navigationContentsAccentColor) + emojiExpandedStatusContent = .premium(color: navigationContentsAccentColor) + case .verified: + emojiRegularStatusContent = .verified(fillColor: presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .large) + emojiExpandedStatusContent = .verified(fillColor: navigationContentsAccentColor, foregroundColor: .clear, sizeType: .large) + case .fake: + emojiRegularStatusContent = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_FakeAccount.uppercased()) + emojiExpandedStatusContent = emojiRegularStatusContent + case .scam: + emojiRegularStatusContent = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_ScamAccount.uppercased()) + emojiExpandedStatusContent = emojiRegularStatusContent + case let .emojiStatus(emojiStatus): + currentEmojiStatus = emojiStatus + emojiRegularStatusContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 80.0, height: 80.0), placeholderColor: presentationData.theme.list.mediaPlaceholderColor, themeColor: navigationContentsAccentColor, loopMode: .forever) + emojiExpandedStatusContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 80.0, height: 80.0), placeholderColor: navigationContentsAccentColor, themeColor: navigationContentsAccentColor, loopMode: .forever) + } + + //let animateStatusIcon = !self.titleCredibilityIconView.bounds.isEmpty + + let iconSize = self.titleCredibilityIconView.update( + transition: Transition(navigationTransition), + component: AnyComponent(EmojiStatusComponent( + context: self.context, + animationCache: self.animationCache, + animationRenderer: self.animationRenderer, + content: emojiRegularStatusContent, + isVisibleForAnimations: true, + useSharedAnimation: true, + action: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.displayPremiumIntro?(strongSelf.titleCredibilityIconView, currentEmojiStatus, strongSelf.emojiStatusFileAndPackTitle.get(), false) + }, + emojiFileUpdated: { [weak self] emojiFile in + guard let strongSelf = self else { + return + } + + if let emojiFile = emojiFile { + strongSelf.emojiStatusFileAndPackTitle.set(.never()) + + for attribute in emojiFile.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute, let packReference = packReference { + strongSelf.emojiStatusPackDisposable.set((strongSelf.context.engine.stickers.loadedStickerPack(reference: packReference, forceActualized: false) + |> filter { result in + if case .result = result { + return true + } else { + return false + } + } + |> mapToSignal { result -> Signal<(TelegramMediaFile, LoadedStickerPack)?, NoError> in + if case let .result(_, items, _) = result { + return .single(items.first.flatMap { ($0.file, result) }) + } else { + return .complete() + } + }).startStrict(next: { fileAndPackTitle in + guard let strongSelf = self else { + return + } + strongSelf.emojiStatusFileAndPackTitle.set(.single(fileAndPackTitle)) + })) + break + } + } + } else { + strongSelf.emojiStatusFileAndPackTitle.set(.never()) + } + } + )), + environment: {}, + containerSize: CGSize(width: 34.0, height: 34.0) + ) + let expandedIconSize = self.titleExpandedCredibilityIconView.update( + transition: Transition(navigationTransition), + component: AnyComponent(EmojiStatusComponent( + context: self.context, + animationCache: self.animationCache, + animationRenderer: self.animationRenderer, + content: emojiExpandedStatusContent, + isVisibleForAnimations: true, + useSharedAnimation: true, + action: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.displayPremiumIntro?(strongSelf.titleExpandedCredibilityIconView, currentEmojiStatus, strongSelf.emojiStatusFileAndPackTitle.get(), true) + } + )), + environment: {}, + containerSize: CGSize(width: 34.0, height: 34.0) + ) + + self.credibilityIconSize = iconSize + self.titleExpandedCredibilityIconSize = expandedIconSize + } + + self.navigationButtonContainer.updateContentsColor(backgroundContentColor: headerButtonBackgroundColor, contentsColor: navigationContentsAccentColor, transition: navigationTransition) + + self.titleNode.updateTintColor(color: navigationContentsPrimaryColor, transition: navigationTransition) + self.subtitleNode.updateTintColor(color: navigationContentsSecondaryColor, transition: navigationTransition) + self.panelSubtitleNode.updateTintColor(color: navigationContentsSecondaryColor, transition: navigationTransition) + if let navigationBar = self.controller?.navigationBar { + if let mainContentNode = navigationBar.backButtonNode.mainContentNode { + navigationTransition.updateTintColor(layer: mainContentNode.layer, color: navigationContentsAccentColor) + } + navigationTransition.updateTintColor(layer: navigationBar.backButtonArrow.layer, color: navigationContentsAccentColor) + + if let mainContentNode = navigationBar.leftButtonNode.mainContentNode { + navigationTransition.updateTintColor(layer: mainContentNode.layer, color: navigationContentsAccentColor) + } + + navigationBar.rightButtonNode.contentsColor = navigationContentsAccentColor + navigationBar.leftButtonNode.contentsColor = navigationContentsAccentColor + navigationBar.backButtonNode.contentsColor = navigationContentsAccentColor + } + + var titleBrightness: CGFloat = 0.0 + navigationContentsPrimaryColor.getHue(nil, saturation: nil, brightness: &titleBrightness, alpha: nil) + self.controller?.setStatusBarStyle(titleBrightness > 0.5 ? .White : .Black, animated: !isFirstTime && animateHeader) + + self.avatarListNode.avatarContainerNode.updateTransitionFraction(transitionFraction, transition: transition) + self.avatarListNode.listContainerNode.currentItemNode?.updateTransitionFraction(transitionFraction, transition: transition) + self.avatarOverlayNode.updateTransitionFraction(transitionFraction, transition: transition) + + if self.navigationTitle != presentationData.strings.EditProfile_Title || themeUpdated { + self.navigationTitleNode.attributedText = NSAttributedString(string: presentationData.strings.EditProfile_Title, font: Font.semibold(17.0), textColor: .white) + } + + let navigationTitleSize = self.navigationTitleNode.updateLayout(CGSize(width: width, height: navigationHeight)) + self.navigationTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - navigationTitleSize.width) / 2.0), y: navigationHeight - 44.0 + floorToScreenPixels((44.0 - navigationTitleSize.height) / 2.0)), size: navigationTitleSize) + + self.navigationBackgroundNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: navigationHeight)) + self.navigationBackgroundBackgroundNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: navigationHeight)) + self.navigationSeparatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: navigationHeight), size: CGSize(width: width, height: UIScreenPixel)) + self.navigationBackgroundBackgroundNode.backgroundColor = presentationData.theme.rootController.navigationBar.opaqueBackgroundColor + self.navigationSeparatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor + + let navigationSeparatorAlpha: CGFloat = state.isEditing && self.isSettings ? min(1.0, contentOffset / (navigationHeight * 0.5)) : 0.0 + transition.updateAlpha(node: self.navigationBackgroundBackgroundNode, alpha: 1.0 - navigationSeparatorAlpha) + transition.updateAlpha(node: self.navigationSeparatorNode, alpha: navigationSeparatorAlpha) + + self.separatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor + + let expandedAvatarControlsHeight: CGFloat = 61.0 + var expandedAvatarListHeight = min(width, containerHeight - expandedAvatarControlsHeight) + if self.isSettings { + expandedAvatarListHeight = expandedAvatarListHeight + 60.0 + } else { + let avatarEnlargementFactor: CGFloat = 1.35 + expandedAvatarListHeight = floor(expandedAvatarListHeight * avatarEnlargementFactor) + } + + let expandedAvatarListSize = CGSize(width: width, height: expandedAvatarListHeight) + + let actionButtonKeys: [PeerInfoHeaderButtonKey] = self.isSettings ? [] : peerInfoHeaderActionButtons(peer: peer, isSecretChat: isSecretChat, isContact: isContact) + let buttonKeys: [PeerInfoHeaderButtonKey] = self.isSettings ? [] : 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 + let titleStringText: String + let smallTitleAttributes: MultiScaleTextState.Attributes + let titleAttributes: MultiScaleTextState.Attributes + let subtitleStringText: String + let smallSubtitleAttributes: MultiScaleTextState.Attributes + let subtitleAttributes: MultiScaleTextState.Attributes + var subtitleIsButton: Bool = false + var panelSubtitleString: (text: String, attributes: MultiScaleTextState.Attributes)? + let usernameString: (text: String, attributes: MultiScaleTextState.Attributes) + if let peer = peer { + isPremium = peer.isPremium + isVerified = peer.isVerified + isFake = peer.isFake || peer.isScam + } + + let titleShadowColor = UIColor(white: 0.0, alpha: 0.1) + + if let peer = peer { + var title: String + if peer.id == self.context.account.peerId && !self.isSettings { + title = presentationData.strings.Conversation_SavedMessages + } else if peer.id == self.context.account.peerId && !self.isSettings { + title = presentationData.strings.DialogList_Replies + } else if let threadData = threadData { + title = threadData.info.title + } else { + title = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + } + title = title.replacingOccurrences(of: "\u{1160}", with: "").replacingOccurrences(of: "\u{3164}", with: "") + if title.isEmpty { + // MARK: Nicegram HidePhone, hidePhoneSettings check added + if let peer = peer as? TelegramUser, let phone = peer.phone, !NGSettings.hidePhoneSettings { + title = formatPhoneNumber(context: self.context, number: phone) + } else if let addressName = peer.addressName { + title = "@\(addressName)" + } else { + title = " " + } + } + + titleStringText = title + titleAttributes = MultiScaleTextState.Attributes(font: Font.medium(28.0), color: .white) + smallTitleAttributes = MultiScaleTextState.Attributes(font: Font.medium(28.0), color: .white, shadowColor: titleShadowColor) + + if self.isSettings, let user = peer as? TelegramUser { + // MARK: Nicegram HidePhone + var formattedPhone = formatPhoneNumber(context: self.context, number: user.phone ?? "") + if !formattedPhone.isEmpty && NGSettings.hidePhoneSettings { + formattedPhone = "" + } + // + + // MARK: Nicegram HidePhone, changed to "= formattedPhone" + var subtitle = formattedPhone + + if let mainUsername = user.addressName, !mainUsername.isEmpty { + subtitle = "\(subtitle) • @\(mainUsername)" + } + subtitleStringText = subtitle + subtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(17.0), color: .white) + smallSubtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white, shadowColor: titleShadowColor) + + usernameString = ("", MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white)) + } else if let _ = threadData { + let subtitleColor: UIColor + subtitleColor = UIColor.white + + let statusText: String + statusText = peer.debugDisplayTitle + + 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 { + let subtitleColor: UIColor + if panelStatusData.isActivity { + subtitleColor = UIColor.white + } else { + subtitleColor = UIColor.white + } + panelSubtitleString = (panelStatusData.text, MultiScaleTextState.Attributes(font: Font.regular(17.0), color: subtitleColor)) + } + } else if let statusData = statusData { + let subtitleColor: UIColor + if statusData.isActivity { + subtitleColor = UIColor.white + } else { + subtitleColor = UIColor.white + } + + subtitleStringText = statusData.text + subtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(17.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)) + + let (maybePanelStatusData, _, _) = panelStatusData + if let panelStatusData = maybePanelStatusData { + let subtitleColor: UIColor + if panelStatusData.isActivity { + subtitleColor = UIColor.white + } else { + subtitleColor = UIColor.white + } + panelSubtitleString = (panelStatusData.text, MultiScaleTextState.Attributes(font: Font.regular(17.0), color: subtitleColor)) + } + } else { + subtitleStringText = " " + subtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white) + smallSubtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white, shadowColor: titleShadowColor) + + usernameString = ("", MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white)) + } + } else { + titleStringText = " " + titleAttributes = MultiScaleTextState.Attributes(font: Font.regular(24.0), color: .white) + smallTitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(24.0), color: .white, shadowColor: titleShadowColor) + + subtitleStringText = " " + subtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white) + smallSubtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white, shadowColor: titleShadowColor) + + usernameString = ("", MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white)) + } + + let textSideInset: CGFloat = 36.0 + let expandedAvatarHeight: CGFloat = expandedAvatarListSize.height + + let titleConstrainedSize = CGSize(width: width - textSideInset * 2.0 - (isPremium || isVerified || isFake ? 20.0 : 0.0), height: .greatestFiniteMagnitude) + + let titleNodeLayout = self.titleNode.updateLayout(text: titleStringText, states: [ + TitleNodeStateRegular: MultiScaleTextState(attributes: titleAttributes, constrainedSize: titleConstrainedSize), + TitleNodeStateExpanded: MultiScaleTextState(attributes: smallTitleAttributes, constrainedSize: titleConstrainedSize) + ], mainState: TitleNodeStateRegular) + + let subtitleNodeLayout = self.subtitleNode.updateLayout(text: subtitleStringText, states: [ + TitleNodeStateRegular: MultiScaleTextState(attributes: subtitleAttributes, constrainedSize: titleConstrainedSize), + TitleNodeStateExpanded: MultiScaleTextState(attributes: smallSubtitleAttributes, constrainedSize: titleConstrainedSize) + ], mainState: TitleNodeStateRegular) + self.subtitleNode.accessibilityLabel = subtitleStringText + + if subtitleIsButton { + let subtitleBackgroundNode: ASDisplayNode + if let current = self.subtitleBackgroundNode { + subtitleBackgroundNode = current + } else { + subtitleBackgroundNode = ASDisplayNode() + self.subtitleBackgroundNode = subtitleBackgroundNode + self.subtitleNode.insertSubnode(subtitleBackgroundNode, at: 0) + } + + let subtitleBackgroundButton: HighlightTrackingButtonNode + if let current = self.subtitleBackgroundButton { + subtitleBackgroundButton = current + } else { + subtitleBackgroundButton = HighlightTrackingButtonNode() + self.subtitleBackgroundButton = subtitleBackgroundButton + self.subtitleNode.addSubnode(subtitleBackgroundButton) + + subtitleBackgroundButton.addTarget(self, action: #selector(self.subtitleBackgroundPressed), forControlEvents: .touchUpInside) + subtitleBackgroundButton.highligthedChanged = { [weak self] highlighted in + guard let self else { + return + } + if highlighted { + self.subtitleNode.layer.removeAnimation(forKey: "opacity") + self.subtitleNode.alpha = 0.4 + } else { + self.subtitleNode.alpha = 1.0 + self.subtitleNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + } + } + } + + let subtitleArrowNode: ASImageNode + if let current = self.subtitleArrowNode { + subtitleArrowNode = current + if themeUpdated { + subtitleArrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Item List/DisclosureArrow"), color: .white)?.withRenderingMode(.alwaysTemplate) + } + } else { + subtitleArrowNode = ASImageNode() + self.subtitleArrowNode = subtitleArrowNode + self.subtitleNode.insertSubnode(subtitleArrowNode, at: 1) + + subtitleArrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Item List/DisclosureArrow"), color: .white)?.withRenderingMode(.alwaysTemplate) + } + subtitleBackgroundNode.backgroundColor = .white.withMultipliedAlpha(0.1) + let subtitleSize = subtitleNodeLayout[TitleNodeStateRegular]!.size + var subtitleBackgroundFrame = CGRect(origin: CGPoint(), size: subtitleSize).offsetBy(dx: -subtitleSize.width * 0.5, dy: -subtitleSize.height * 0.5).insetBy(dx: -6.0, dy: -4.0) + subtitleBackgroundFrame.size.width += 12.0 + transition.updateFrame(node: subtitleBackgroundNode, frame: subtitleBackgroundFrame) + transition.updateCornerRadius(node: subtitleBackgroundNode, cornerRadius: subtitleBackgroundFrame.height * 0.5) + + transition.updateFrame(node: subtitleBackgroundButton, frame: subtitleBackgroundFrame) + + if let arrowImage = subtitleArrowNode.image { + let scaleFactor: CGFloat = 0.8 + let arrowSize = CGSize(width: floorToScreenPixels(arrowImage.size.width * scaleFactor), height: floorToScreenPixels(arrowImage.size.height * scaleFactor)) + subtitleArrowNode.frame = CGRect(origin: CGPoint(x: subtitleBackgroundFrame.maxX - arrowSize.width - 1.0, y: subtitleBackgroundFrame.minY + floor((subtitleBackgroundFrame.height - arrowSize.height) / 2.0)), size: arrowSize) + } + } else { + if let subtitleBackgroundNode = self.subtitleBackgroundNode { + self.subtitleBackgroundNode = nil + subtitleBackgroundNode.removeFromSupernode() + } + if let subtitleArrowNode = self.subtitleArrowNode { + self.subtitleArrowNode = nil + subtitleArrowNode.removeFromSupernode() + } + if let subtitleBackgroundButton = self.subtitleBackgroundButton { + self.subtitleBackgroundButton = nil + subtitleBackgroundButton.removeFromSupernode() + } + } + + 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 + + self.panelSubtitleNode.view.superview?.addSubview(snapshotView) + snapshotView.frame = self.panelSubtitleNode.frame + snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 100.0 * direction, y: 0.0), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) + + self.panelSubtitleNode.layer.animatePosition(from: CGPoint(x: 100.0 * direction * -1.0, y: 0.0), to: CGPoint(), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + self.panelSubtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + } + } + + let panelSubtitleNodeLayout = self.panelSubtitleNode.updateLayout(text: panelSubtitleString?.text ?? subtitleStringText, states: [ + TitleNodeStateRegular: MultiScaleTextState(attributes: panelSubtitleString?.attributes ?? subtitleAttributes, constrainedSize: titleConstrainedSize), + TitleNodeStateExpanded: MultiScaleTextState(attributes: panelSubtitleString?.attributes ?? subtitleAttributes, constrainedSize: titleConstrainedSize) + ], mainState: TitleNodeStateRegular) + self.panelSubtitleNode.accessibilityLabel = panelSubtitleString?.text ?? subtitleStringText + + let usernameNodeLayout = self.usernameNode.updateLayout(text: usernameString.text, states: [ + TitleNodeStateRegular: MultiScaleTextState(attributes: usernameString.attributes, constrainedSize: CGSize(width: titleConstrainedSize.width, height: titleConstrainedSize.height)), + TitleNodeStateExpanded: MultiScaleTextState(attributes: usernameString.attributes, constrainedSize: CGSize(width: width - titleNodeLayout[TitleNodeStateExpanded]!.size.width - 8.0, height: titleConstrainedSize.height)) + ], mainState: TitleNodeStateRegular) + self.usernameNode.accessibilityLabel = usernameString.text + + let avatarCenter: CGPoint + if let transitionSourceAvatarFrame = transitionSourceAvatarFrame { + avatarCenter = CGPoint(x: (1.0 - transitionFraction) * avatarFrame.midX + transitionFraction * transitionSourceAvatarFrame.midX, y: (1.0 - transitionFraction) * avatarFrame.midY + transitionFraction * transitionSourceAvatarFrame.midY) + } else { + avatarCenter = avatarFrame.center + } + + let titleSize = titleNodeLayout[TitleNodeStateRegular]!.size + let titleExpandedSize = titleNodeLayout[TitleNodeStateExpanded]!.size + let subtitleSize = subtitleNodeLayout[TitleNodeStateRegular]!.size + let _ = panelSubtitleNodeLayout[TitleNodeStateRegular]!.size + let usernameSize = usernameNodeLayout[TitleNodeStateRegular]!.size + + var titleHorizontalOffset: CGFloat = 0.0 + if let credibilityIconSize = self.credibilityIconSize, let titleExpandedCredibilityIconSize = self.titleExpandedCredibilityIconSize { + titleHorizontalOffset = -(credibilityIconSize.width + 4.0) / 2.0 + + var collapsedTransitionOffset: CGFloat = 0.0 + if let navigationTransition = self.navigationTransition { + collapsedTransitionOffset = -10.0 * navigationTransition.fraction + } + + transition.updateFrame(view: self.titleCredibilityIconView, frame: CGRect(origin: CGPoint(x: titleSize.width + 4.0 + collapsedTransitionOffset, y: floor((titleSize.height - credibilityIconSize.height) / 2.0)), size: credibilityIconSize)) + transition.updateFrame(view: self.titleExpandedCredibilityIconView, frame: CGRect(origin: CGPoint(x: titleExpandedSize.width + 4.0, y: floor((titleExpandedSize.height - titleExpandedCredibilityIconSize.height) / 2.0) + 1.0), size: titleExpandedCredibilityIconSize)) + } + + var titleFrame: CGRect + var subtitleFrame: CGRect + let usernameFrame: CGRect + let usernameSpacing: CGFloat = 4.0 + + let expandedTitleScale: CGFloat = 0.8 + + var bottomShadowHeight: CGFloat = 88.0 + if !self.isSettings { + bottomShadowHeight += 110.0 + } + let bottomShadowFrame = CGRect(origin: CGPoint(x: 0.0, y: expandedAvatarHeight - bottomShadowHeight), size: CGSize(width: width, height: bottomShadowHeight)) + transition.updateFrame(node: self.avatarListNode.listContainerNode.bottomShadowNode, frame: bottomShadowFrame, beginWithCurrentState: true) + self.avatarListNode.listContainerNode.bottomShadowNode.update(size: bottomShadowFrame.size, transition: transition) + + 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) + if !self.isSettings { + minTitleFrame.origin.y -= 83.0 + } + + titleFrame = CGRect(origin: CGPoint(x: minTitleFrame.midX - titleSize.width / 2.0, y: minTitleFrame.midY - titleSize.height / 2.0), size: titleSize) + subtitleFrame = CGRect(origin: CGPoint(x: 16.0, y: minTitleFrame.maxY + 2.0), size: subtitleSize) + usernameFrame = CGRect(origin: CGPoint(x: width - usernameSize.width - 16.0, y: minTitleFrame.midY - usernameSize.height / 2.0), size: usernameSize) + } else { + titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 9.0 + (subtitleSize.height.isZero ? 11.0 : 0.0)), size: titleSize) + + let totalSubtitleWidth = subtitleSize.width + usernameSpacing + usernameSize.width + if usernameSize.width == 0.0 { + subtitleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize) + usernameFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - usernameSize.width) / 2.0), y: subtitleFrame.maxY + 1.0), size: usernameSize) + } else { + subtitleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - totalSubtitleWidth) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize) + usernameFrame = CGRect(origin: CGPoint(x: subtitleFrame.maxX + usernameSpacing, y: titleFrame.maxY + 1.0), size: usernameSize) + } + } + + let singleTitleLockOffset: CGFloat = (peer?.id == self.context.account.peerId || subtitleSize.height.isZero) ? 8.0 : 0.0 + + let titleLockOffset: CGFloat = 7.0 + singleTitleLockOffset + let titleMaxLockOffset: CGFloat = 7.0 + var titleCollapseOffset = titleFrame.midY - statusBarHeight - titleLockOffset + if case .regular = metrics.widthClass, !isSettings { + titleCollapseOffset -= 7.0 + } + let titleOffset = -min(titleCollapseOffset, contentOffset) + let titleCollapseFraction = max(0.0, min(1.0, contentOffset / titleCollapseOffset)) + + let titleMinScale: CGFloat = 0.6 + let subtitleMinScale: CGFloat = 0.8 + let avatarMinScale: CGFloat = 0.55 + + let apparentTitleLockOffset = (1.0 - titleCollapseFraction) * 0.0 + titleCollapseFraction * titleMaxLockOffset + + let paneAreaExpansionDistance: CGFloat = 32.0 + let effectiveAreaExpansionFraction: CGFloat + if state.isEditing { + effectiveAreaExpansionFraction = 0.0 + } else if isSettings { + var paneAreaExpansionDelta = (self.frame.maxY - navigationHeight) - contentOffset + paneAreaExpansionDelta = max(0.0, min(paneAreaExpansionDelta, paneAreaExpansionDistance)) + effectiveAreaExpansionFraction = 1.0 - paneAreaExpansionDelta / paneAreaExpansionDistance + } else { + var paneAreaExpansionDelta = (paneContainerY - navigationHeight) - contentOffset + paneAreaExpansionDelta = max(0.0, min(paneAreaExpansionDelta, paneAreaExpansionDistance)) + effectiveAreaExpansionFraction = 1.0 - paneAreaExpansionDelta / paneAreaExpansionDistance + } + + let secondarySeparatorAlpha = 1.0 - effectiveAreaExpansionFraction + if self.navigationTransition == nil && !self.isSettings && effectiveSeparatorAlpha == 1.0 && secondarySeparatorAlpha < 1.0 { + effectiveSeparatorAlpha = secondarySeparatorAlpha + } + transition.updateAlpha(node: self.separatorNode, alpha: effectiveSeparatorAlpha) + + self.titleNode.update(stateFractions: [ + TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0, + TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0 + ], transition: transition) + + let subtitleAlpha: CGFloat + var subtitleOffset: CGFloat = 0.0 + let panelSubtitleAlpha: CGFloat + var panelSubtitleOffset: CGFloat = 0.0 + if self.isSettings { + subtitleAlpha = 1.0 - titleCollapseFraction + panelSubtitleAlpha = 0.0 + } else { + if (panelSubtitleString?.text ?? subtitleStringText) != subtitleStringText { + subtitleAlpha = 1.0 - effectiveAreaExpansionFraction + panelSubtitleAlpha = effectiveAreaExpansionFraction + + subtitleOffset = -effectiveAreaExpansionFraction * 5.0 + panelSubtitleOffset = (1.0 - effectiveAreaExpansionFraction) * 5.0 + } else { + if self.navigationTransition != nil { + subtitleAlpha = 1.0 + panelSubtitleAlpha = 0.0 + } else { + if effectiveAreaExpansionFraction == 1.0 { + subtitleAlpha = 0.0 + panelSubtitleAlpha = 1.0 + } else { + subtitleAlpha = 1.0 + panelSubtitleAlpha = 0.0 + } + } + } + } + self.subtitleNode.update(stateFractions: [ + TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0, + TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0 + ], alpha: subtitleAlpha, transition: transition) + + self.panelSubtitleNode.update(stateFractions: [ + TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0, + TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0 + ], alpha: panelSubtitleAlpha, transition: transition) + + self.usernameNode.update(stateFractions: [ + TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0, + TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0 + ], alpha: subtitleAlpha, transition: transition) + + let avatarScale: CGFloat + let avatarOffset: CGFloat + if self.navigationTransition != nil { + if let transitionSourceAvatarFrame = transitionSourceAvatarFrame { + var trueAvatarSize = transitionSourceAvatarFrame.size + if let storyStats = self.avatarListNode.avatarContainerNode.avatarNode.storyStats, storyStats.unseenCount != 0 { + trueAvatarSize.width -= 1.33 * 4.0 + trueAvatarSize.height -= 1.33 * 4.0 + } + + avatarScale = ((1.0 - transitionFraction) * avatarFrame.width + transitionFraction * trueAvatarSize.width) / avatarFrame.width + } else { + avatarScale = 1.0 + } + avatarOffset = 0.0 + } else { + // + avatarScale = 1.0 * (1.0 - titleCollapseFraction) + avatarMinScale * titleCollapseFraction + avatarOffset = apparentTitleLockOffset + 0.0 * (1.0 - titleCollapseFraction) + 10.0 * titleCollapseFraction + } + + if let previousAvatarView = self.navigationTransition?.previousAvatarView, let transitionSourceAvatarFrame { + let previousScale = ((1.0 - transitionFraction) * avatarFrame.width + transitionFraction * transitionSourceAvatarFrame.width) / transitionSourceAvatarFrame.width + + transition.updateAlpha(layer: previousAvatarView.layer, alpha: transitionFraction) + transition.updateTransformScale(layer: previousAvatarView.layer, scale: previousScale) + transition.updatePosition(layer: previousAvatarView.layer, position: self.view.convert(CGPoint(x: avatarCenter.x - (27.0 * (1.0 - transitionFraction) + 10 * transitionFraction), y: avatarCenter.y - (2.66 * (1.0 - transitionFraction) + 1.0 * transitionFraction)), to: previousAvatarView.superview)) + } + + if subtitleIsButton { + subtitleFrame.origin.y += 11.0 * (1.0 - titleCollapseFraction) + if let subtitleBackgroundButton = self.subtitleBackgroundButton { + transition.updateAlpha(node: subtitleBackgroundButton, alpha: (1.0 - titleCollapseFraction)) + } + if let subtitleBackgroundNode = self.subtitleBackgroundNode { + transition.updateAlpha(node: subtitleBackgroundNode, alpha: (1.0 - titleCollapseFraction)) + } + if let subtitleArrowNode = self.subtitleArrowNode { + transition.updateAlpha(node: subtitleArrowNode, alpha: (1.0 - titleCollapseFraction)) + } + } + + let avatarCornerRadius: CGFloat = isForum ? floor(avatarSize * 0.25) : avatarSize / 2.0 + + if self.isAvatarExpanded { + self.avatarListNode.listContainerNode.isHidden = false + if let transitionSourceAvatarFrame = transitionSourceAvatarFrame { + var trueAvatarSize = transitionSourceAvatarFrame.size + if let storyStats = self.avatarListNode.avatarContainerNode.avatarNode.storyStats, storyStats.unseenCount != 0 { + trueAvatarSize.width -= 1.33 * 4.0 + trueAvatarSize.height -= 1.33 * 4.0 + } + + transition.updateCornerRadius(node: self.avatarListNode.listContainerNode, cornerRadius: transitionFraction * trueAvatarSize.width / 2.0) + transition.updateCornerRadius(node: self.avatarListNode.listContainerNode.controlsClippingNode, cornerRadius: transitionFraction * trueAvatarSize.width / 2.0) + } else { + transition.updateCornerRadius(node: self.avatarListNode.listContainerNode, cornerRadius: 0.0) + transition.updateCornerRadius(node: self.avatarListNode.listContainerNode.controlsClippingNode, cornerRadius: 0.0) + } + } else if self.avatarListNode.listContainerNode.cornerRadius != avatarCornerRadius { + transition.updateCornerRadius(node: self.avatarListNode.listContainerNode.controlsClippingNode, cornerRadius: avatarCornerRadius) + transition.updateCornerRadius(node: self.avatarListNode.listContainerNode, cornerRadius: avatarCornerRadius, completion: { [weak self] _ in + guard let strongSelf = self else { + return + } + strongSelf.avatarListNode.avatarContainerNode.canAttachVideo = true + strongSelf.avatarListNode.listContainerNode.isHidden = true + if !strongSelf.skipCollapseCompletion { + DispatchQueue.main.async { + strongSelf.avatarListNode.listContainerNode.isCollapsing = false + } + } + }) + } + + self.avatarListNode.update(size: CGSize(), avatarSize: avatarSize, isExpanded: self.isAvatarExpanded, peer: peer, isForum: isForum, threadId: self.forumTopicThreadId, threadInfo: threadData?.info, theme: presentationData.theme, transition: transition) + self.editingContentNode.avatarNode.update(peer: peer, threadData: threadData, chatLocation: self.chatLocation, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing) + self.avatarOverlayNode.update(peer: peer, threadData: threadData, chatLocation: self.chatLocation, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing) + if additive { + transition.updateSublayerTransformScaleAdditive(node: self.avatarListNode.avatarContainerNode, scale: avatarScale) + transition.updateSublayerTransformScaleAdditive(node: self.avatarOverlayNode, scale: avatarScale) + } else { + transition.updateSublayerTransformScale(node: self.avatarListNode.avatarContainerNode, scale: avatarScale) + transition.updateSublayerTransformScale(node: self.avatarOverlayNode, scale: avatarScale) + } + + if let avatarStoryView = self.avatarListNode.avatarContainerNode.avatarStoryView?.view { + transition.updateAlpha(layer: avatarStoryView.layer, alpha: 1.0 - transitionFraction) + } + + var apparentAvatarFrame: CGRect + var apparentAvatarListFrame: CGRect + let controlsClippingFrame: CGRect + if self.isAvatarExpanded { + let expandedAvatarCenter = CGPoint(x: expandedAvatarListSize.width / 2.0, y: expandedAvatarListSize.width / 2.0 - contentOffset / 2.0) + apparentAvatarFrame = CGRect(origin: CGPoint(x: expandedAvatarCenter.x * (1.0 - transitionFraction) + transitionFraction * avatarCenter.x, y: expandedAvatarCenter.y * (1.0 - transitionFraction) + transitionFraction * avatarCenter.y), size: CGSize()) + + let expandedAvatarListCenter = CGPoint(x: expandedAvatarListSize.width / 2.0, y: expandedAvatarListSize.height / 2.0 - contentOffset / 2.0) + apparentAvatarListFrame = CGRect(origin: CGPoint(x: expandedAvatarListCenter.x * (1.0 - transitionFraction) + transitionFraction * avatarCenter.x, y: expandedAvatarListCenter.y * (1.0 - transitionFraction) + transitionFraction * avatarCenter.y), size: CGSize()) + + if let transitionSourceAvatarFrame = transitionSourceAvatarFrame { + var trueAvatarSize = transitionSourceAvatarFrame.size + if let storyStats = self.avatarListNode.avatarContainerNode.avatarNode.storyStats, storyStats.unseenCount != 0 { + trueAvatarSize.width -= 1.33 * 4.0 + trueAvatarSize.height -= 1.33 * 4.0 + } + let trueAvatarFrame = trueAvatarSize.centered(around: transitionSourceAvatarFrame.center) + + let expandedFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: expandedAvatarListSize) + controlsClippingFrame = CGRect(origin: CGPoint(x: transitionFraction * trueAvatarFrame.minX + (1.0 - transitionFraction) * expandedFrame.minX, y: transitionFraction * trueAvatarFrame.minY + (1.0 - transitionFraction) * expandedFrame.minY), size: CGSize(width: transitionFraction * trueAvatarFrame.width + (1.0 - transitionFraction) * expandedFrame.width, height: transitionFraction * trueAvatarFrame.height + (1.0 - transitionFraction) * expandedFrame.height)) + } else { + controlsClippingFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: expandedAvatarListSize) + } + } else { + var trueAvatarSize = avatarFrame.size + if let storyStats = self.avatarListNode.avatarContainerNode.avatarNode.storyStats, storyStats.totalCount != 0 { + trueAvatarSize.width -= 3.0 * 4.0 + trueAvatarSize.height -= 3.0 * 4.0 + } + apparentAvatarFrame = CGRect(origin: CGPoint(x: avatarCenter.x - trueAvatarSize.width / 2.0, y: -contentOffset + avatarOffset + avatarCenter.y - trueAvatarSize.height / 2.0), size: trueAvatarSize) + apparentAvatarListFrame = apparentAvatarFrame + controlsClippingFrame = apparentAvatarFrame + } + + let avatarClipOffset: CGFloat = !self.isAvatarExpanded && deviceMetrics.hasDynamicIsland && self.avatarClippingNode.clipsToBounds && !isLandscape ? 48.0 : 0.0 + let clippingNodeTransition = ContainedViewLayoutTransition.immediate + clippingNodeTransition.updateFrame(layer: self.avatarClippingNode.layer, frame: CGRect(origin: CGPoint(x: 0.0, y: avatarClipOffset), size: CGSize(width: width, height: 1000.0))) + clippingNodeTransition.updateSublayerTransformOffset(layer: self.avatarClippingNode.layer, offset: CGPoint(x: 0.0, y: -avatarClipOffset)) + let clippingNodeRadiusTransition = ContainedViewLayoutTransition.animated(duration: 0.15, curve: .easeInOut) + clippingNodeRadiusTransition.updateCornerRadius(node: self.avatarClippingNode, cornerRadius: avatarClipOffset > 0.0 ? width / 2.5 : 0.0) + + let _ = apparentAvatarListFrame + transition.updateFrameAdditive(node: self.avatarListNode, frame: CGRect(origin: apparentAvatarFrame.center, size: CGSize())) + transition.updateFrameAdditive(node: self.avatarOverlayNode, frame: CGRect(origin: apparentAvatarFrame.center, size: CGSize())) + + let avatarListContainerFrame: CGRect + let avatarListContainerScale: CGFloat + if self.isAvatarExpanded { + if let transitionSourceAvatarFrame = transitionSourceAvatarFrame { + let neutralAvatarListContainerSize = expandedAvatarListSize + var avatarListContainerSize = CGSize(width: neutralAvatarListContainerSize.width * (1.0 - transitionFraction) + transitionSourceAvatarFrame.width * transitionFraction, height: neutralAvatarListContainerSize.height * (1.0 - transitionFraction) + transitionSourceAvatarFrame.height * transitionFraction) + + if let storyStats = self.avatarListNode.avatarContainerNode.avatarNode.storyStats, storyStats.unseenCount != 0 { + avatarListContainerSize.width -= 1.33 * 5.0 + avatarListContainerSize.height -= 1.33 * 5.0 + } + + avatarListContainerFrame = CGRect(origin: CGPoint(x: -avatarListContainerSize.width / 2.0, y: -avatarListContainerSize.width / 2.0), size: avatarListContainerSize) + } else { + avatarListContainerFrame = CGRect(origin: CGPoint(x: -expandedAvatarListSize.width / 2.0, y: -expandedAvatarListSize.width / 2.0), size: expandedAvatarListSize) + } + avatarListContainerScale = 1.0 + max(0.0, -contentOffset / avatarListContainerFrame.width) + } else { + let expandHeightFraction = expandedAvatarListSize.height / expandedAvatarListSize.width + avatarListContainerFrame = CGRect(origin: CGPoint(x: -apparentAvatarFrame.width / 2.0, y: -apparentAvatarFrame.width / 2.0 + expandHeightFraction * 0.0 * apparentAvatarFrame.width), size: apparentAvatarFrame.size) + avatarListContainerScale = avatarScale + } + transition.updateFrame(node: self.avatarListNode.listContainerNode, frame: avatarListContainerFrame) + let innerScale = avatarListContainerFrame.width / expandedAvatarListSize.width + let innerDeltaX = (avatarListContainerFrame.width - expandedAvatarListSize.width) / 2.0 + var innerDeltaY = (avatarListContainerFrame.height - expandedAvatarListSize.height) / 2.0 + if !self.isAvatarExpanded { + innerDeltaY += (expandedAvatarListSize.height - expandedAvatarListSize.width) * 0.5 + } + transition.updateSublayerTransformScale(node: self.avatarListNode.listContainerNode, scale: innerScale) + transition.updateFrameAdditive(node: self.avatarListNode.listContainerNode.contentNode, frame: CGRect(origin: CGPoint(x: innerDeltaX + expandedAvatarListSize.width / 2.0, y: innerDeltaY + expandedAvatarListSize.height / 2.0), size: CGSize())) + self.avatarListNode.listContainerNode.contentNode.update(size: expandedAvatarListSize) + + transition.updateFrameAdditive(node: self.avatarListNode.listContainerNode.controlsClippingOffsetNode, frame: CGRect(origin: controlsClippingFrame.center, size: CGSize())) + transition.updateFrame(node: self.avatarListNode.listContainerNode.controlsClippingNode, frame: CGRect(origin: CGPoint(x: -controlsClippingFrame.width / 2.0, y: -controlsClippingFrame.height / 2.0), size: controlsClippingFrame.size)) + transition.updateFrameAdditive(node: self.avatarListNode.listContainerNode.controlsContainerNode, frame: CGRect(origin: CGPoint(x: -controlsClippingFrame.minX, y: -controlsClippingFrame.minY), size: CGSize(width: expandedAvatarListSize.width, height: expandedAvatarListSize.height))) + + transition.updateFrame(node: self.avatarListNode.listContainerNode.topShadowNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: expandedAvatarListSize.width, height: navigationHeight + 20.0))) + transition.updateFrame(node: self.avatarListNode.listContainerNode.stripContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: statusBarHeight < 25.0 ? (statusBarHeight + 2.0) : (statusBarHeight - 3.0)), size: CGSize(width: expandedAvatarListSize.width, height: 2.0))) + transition.updateFrame(node: self.avatarListNode.listContainerNode.highlightContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: expandedAvatarListSize.width, height: expandedAvatarListSize.height))) + transition.updateAlpha(node: self.avatarListNode.listContainerNode.controlsContainerNode, alpha: self.isAvatarExpanded ? (1.0 - transitionFraction) : 0.0) + + if additive { + transition.updateSublayerTransformScaleAdditive(node: self.avatarListNode.listContainerTransformNode, scale: avatarListContainerScale) + } else { + transition.updateSublayerTransformScale(node: self.avatarListNode.listContainerTransformNode, scale: avatarListContainerScale) + } + + if deviceMetrics.hasDynamicIsland && self.forumTopicThreadId == nil && self.navigationTransition == nil && !isLandscape { + let maskValue = max(0.0, min(1.0, contentOffset / 120.0)) + self.avatarListNode.containerNode.view.mask = self.avatarListNode.maskNode.view + if maskValue > 0.03 { + self.avatarListNode.bottomCoverNode.isHidden = false + self.avatarListNode.topCoverNode.isHidden = false + self.avatarListNode.maskNode.backgroundColor = .clear + } else { + self.avatarListNode.bottomCoverNode.isHidden = true + self.avatarListNode.topCoverNode.isHidden = true + self.avatarListNode.maskNode.backgroundColor = .white + } + self.avatarListNode.topCoverNode.update(maskValue) + self.avatarListNode.maskNode.update(maskValue) + self.avatarListNode.bottomCoverNode.backgroundColor = UIColor(white: 0.0, alpha: maskValue) + + self.avatarListNode.listContainerNode.topShadowNode.isHidden = !self.isAvatarExpanded + + var avatarMaskOffset: CGFloat = 0.0 + if contentOffset < 0.0 { + avatarMaskOffset -= contentOffset + } + + self.avatarListNode.maskNode.position = CGPoint(x: 0.0, y: -self.avatarListNode.frame.minY + 48.0 + 85.0 + avatarMaskOffset) + self.avatarListNode.maskNode.bounds = CGRect(origin: .zero, size: CGSize(width: 171.0, height: 171.0)) + + self.avatarListNode.bottomCoverNode.position = self.avatarListNode.maskNode.position + self.avatarListNode.bottomCoverNode.bounds = self.avatarListNode.maskNode.bounds + + self.avatarListNode.topCoverNode.position = self.avatarListNode.maskNode.position + self.avatarListNode.topCoverNode.bounds = self.avatarListNode.maskNode.bounds + } else { + self.avatarListNode.bottomCoverNode.isHidden = true + self.avatarListNode.topCoverNode.isHidden = true + self.avatarListNode.containerNode.view.mask = nil + } + + self.avatarListNode.listContainerNode.update(size: expandedAvatarListSize, peer: peer.flatMap(EnginePeer.init), isExpanded: self.isAvatarExpanded, transition: transition) + if self.avatarListNode.listContainerNode.isCollapsing && !self.ignoreCollapse { + self.avatarListNode.avatarContainerNode.canAttachVideo = false + } + + let rawHeight: CGFloat + let height: CGFloat + let maxY: CGFloat + let backgroundHeight: CGFloat + if self.isAvatarExpanded { + rawHeight = expandedAvatarHeight + height = max(navigationHeight, rawHeight - contentOffset) + maxY = height - 98.0 + backgroundHeight = height + } else { + rawHeight = navigationHeight + panelWithAvatarHeight + var expandablePart: CGFloat = panelWithAvatarHeight - contentOffset + if self.isSettings { + expandablePart += 20.0 + } else { + if peer?.id == self.context.account.peerId { + expandablePart = 0.0 + } else { + expandablePart += 99.0 + } + } + height = navigationHeight + max(0.0, expandablePart) + maxY = navigationHeight + panelWithAvatarHeight - contentOffset + backgroundHeight = height + } + let _ = maxY + + let apparentHeight = (1.0 - transitionFraction) * backgroundHeight + transitionFraction * transitionSourceHeight + let apparentBackgroundHeight = (1.0 - transitionFraction) * backgroundHeight + transitionFraction * transitionSourceHeight + + if !titleSize.width.isZero && !titleSize.height.isZero { + if self.navigationTransition != nil { + var neutralTitleScale: CGFloat = 1.0 + var neutralSubtitleScale: CGFloat = 1.0 + if self.isAvatarExpanded { + neutralTitleScale = expandedTitleScale + neutralSubtitleScale = 1.0 + } + + let titleScale = (transitionFraction * transitionSourceTitleFrame.height + (1.0 - transitionFraction) * titleFrame.height * neutralTitleScale) / (titleFrame.height) + let subtitleScale = max(0.01, min(10.0, (transitionFraction * transitionSourceSubtitleFrame.height + (1.0 - transitionFraction) * subtitleFrame.height * neutralSubtitleScale) / (subtitleFrame.height))) + + var titleFrame = titleFrame + if !self.isAvatarExpanded { + titleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? 0.0 : titleHorizontalOffset * titleScale, dy: 0.0) + } + + let titleCenter = CGPoint(x: transitionFraction * transitionSourceTitleFrame.midX + (1.0 - transitionFraction) * titleFrame.midX, y: transitionFraction * transitionSourceTitleFrame.midY + (1.0 - transitionFraction) * titleFrame.midY) + let subtitleCenter = CGPoint(x: transitionFraction * transitionSourceSubtitleFrame.midX + (1.0 - transitionFraction) * subtitleFrame.midX, y: transitionFraction * transitionSourceSubtitleFrame.midY + (1.0 - transitionFraction) * subtitleFrame.midY) + + let rawTitleFrame = CGRect(origin: CGPoint(x: titleCenter.x - titleFrame.size.width * neutralTitleScale / 2.0, y: titleCenter.y - titleFrame.size.height * neutralTitleScale / 2.0), size: CGSize(width: titleFrame.size.width * neutralTitleScale, height: titleFrame.size.height * neutralTitleScale)) + self.titleNodeRawContainer.frame = rawTitleFrame + transition.updateFrameAdditiveToCenter(node: self.titleNodeContainer, frame: CGRect(origin: rawTitleFrame.center, size: CGSize())) + transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(), size: CGSize())) + let rawSubtitleFrame = CGRect(origin: CGPoint(x: subtitleCenter.x - subtitleFrame.size.width / 2.0, y: subtitleCenter.y - subtitleFrame.size.height / 2.0), size: subtitleFrame.size) + self.subtitleNodeRawContainer.frame = rawSubtitleFrame + transition.updateFrameAdditiveToCenter(node: self.subtitleNodeContainer, frame: CGRect(origin: rawSubtitleFrame.center, size: CGSize())) + transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: subtitleOffset), size: CGSize())) + transition.updateFrame(node: self.panelSubtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelSubtitleOffset - 1.0), size: CGSize())) + transition.updateFrame(node: self.usernameNode, frame: CGRect(origin: CGPoint(), size: CGSize())) + transition.updateSublayerTransformScale(node: self.titleNodeContainer, scale: titleScale) + transition.updateSublayerTransformScale(node: self.subtitleNodeContainer, scale: subtitleScale) + transition.updateSublayerTransformScale(node: self.usernameNodeContainer, scale: subtitleScale) + } else { + let titleScale: CGFloat + let subtitleScale: CGFloat + var subtitleOffset: CGFloat = 0.0 + if self.isAvatarExpanded { + titleScale = expandedTitleScale + subtitleScale = 1.0 + } else { + titleScale = (1.0 - titleCollapseFraction) * 1.0 + titleCollapseFraction * titleMinScale + subtitleScale = (1.0 - titleCollapseFraction) * 1.0 + titleCollapseFraction * subtitleMinScale + subtitleOffset = titleCollapseFraction * -1.0 + } + + let rawTitleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? 0.0 : titleHorizontalOffset * titleScale, dy: 0.0) + self.titleNodeRawContainer.frame = rawTitleFrame + transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(), size: CGSize())) + let rawSubtitleFrame = subtitleFrame + self.subtitleNodeRawContainer.frame = rawSubtitleFrame + let rawUsernameFrame = usernameFrame + self.usernameNodeRawContainer.frame = rawUsernameFrame + if self.isAvatarExpanded { + transition.updateFrameAdditive(node: self.titleNodeContainer, frame: CGRect(origin: rawTitleFrame.center, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset + apparentTitleLockOffset)) + transition.updateFrameAdditive(node: self.subtitleNodeContainer, frame: CGRect(origin: rawSubtitleFrame.center, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset)) + transition.updateFrameAdditive(node: self.usernameNodeContainer, frame: CGRect(origin: rawUsernameFrame.center, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset)) + } else { + transition.updateFrameAdditiveToCenter(node: self.titleNodeContainer, frame: CGRect(origin: rawTitleFrame.center, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset + apparentTitleLockOffset)) + + var subtitleCenter = rawSubtitleFrame.center + subtitleCenter.x = rawTitleFrame.center.x + (subtitleCenter.x - rawTitleFrame.center.x) * subtitleScale + subtitleCenter.y += subtitleOffset + transition.updateFrameAdditiveToCenter(node: self.subtitleNodeContainer, frame: CGRect(origin: subtitleCenter, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset)) + + var usernameCenter = rawUsernameFrame.center + usernameCenter.x = rawTitleFrame.center.x + (usernameCenter.x - rawTitleFrame.center.x) * subtitleScale + transition.updateFrameAdditiveToCenter(node: self.usernameNodeContainer, frame: CGRect(origin: usernameCenter, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset)) + } + transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: subtitleOffset), size: CGSize())) + transition.updateFrame(node: self.panelSubtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelSubtitleOffset - 1.0), size: CGSize())) + transition.updateFrame(node: self.usernameNode, frame: CGRect(origin: CGPoint(), size: CGSize())) + transition.updateSublayerTransformScaleAdditive(node: self.titleNodeContainer, scale: titleScale) + transition.updateSublayerTransformScaleAdditive(node: self.subtitleNodeContainer, scale: subtitleScale) + transition.updateSublayerTransformScaleAdditive(node: self.usernameNodeContainer, scale: subtitleScale) + } + } + + let buttonsTransitionDistance: CGFloat = -min(0.0, apparentBackgroundHeight - backgroundHeight) + let buttonsTransitionDistanceNorm: CGFloat = 40.0 + + let innerContentOffset = max(0.0, contentOffset - 140.0) + let backgroundTransitionFraction: CGFloat = 1.0 - max(0.0, min(1.0, innerContentOffset / 30.0)) + + let innerButtonsTransitionStepDistance: CGFloat = 58.0 + let innerButtonsTransitionStepInset: CGFloat = 28.0 + let innerButtonsTransitionDistance: CGFloat = navigationHeight + panelWithAvatarHeight - innerButtonsTransitionStepDistance - innerButtonsTransitionStepInset + let innerButtonsContentOffset = max(0.0, contentOffset - innerButtonsTransitionDistance) + let innerButtonsTransitionFraction = max(0.0, min(1.0, innerButtonsContentOffset / innerButtonsTransitionStepDistance)) + + let buttonsTransitionFraction: CGFloat = 1.0 - max(0.0, min(1.0, buttonsTransitionDistance / buttonsTransitionDistanceNorm)) + + let buttonSpacing: CGFloat = 8.0 + let buttonSideInset = max(16.0, containerInset) + + let actionButtonWidth = (width - buttonSideInset * 2.0 + buttonSpacing) / CGFloat(actionButtonKeys.count) - buttonSpacing + let actionButtonSize = CGSize(width: actionButtonWidth, height: 40.0) + var actionButtonRightOrigin = CGPoint(x: width - buttonSideInset, y: backgroundHeight - 16.0 - actionButtonSize.height) + + for buttonKey in actionButtonKeys.reversed() { + let buttonNode: PeerInfoHeaderActionButtonNode + var wasAdded = false + if let current = self.actionButtonNodes[buttonKey] { + buttonNode = current + } else { + wasAdded = true + buttonNode = PeerInfoHeaderActionButtonNode(key: buttonKey, action: { [weak self] buttonNode, gesture in + self?.actionButtonPressed(buttonNode, gesture: gesture) + }) + self.actionButtonNodes[buttonKey] = buttonNode + self.buttonsContainerNode.addSubnode(buttonNode) + } + + let buttonFrame = CGRect(origin: CGPoint(x: actionButtonRightOrigin.x - actionButtonSize.width, y: actionButtonRightOrigin.y), size: actionButtonSize) + let buttonTransition: ContainedViewLayoutTransition = wasAdded ? .immediate : transition + + if additive { + buttonTransition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) + } else { + buttonTransition.updateFrame(node: buttonNode, frame: buttonFrame) + } + let buttonText: String + switch buttonKey { + case .message: + buttonText = "Message" + case .addContact: + buttonText = "Add" + default: + fatalError() + } + + buttonNode.update(size: buttonFrame.size, text: buttonText, presentationData: presentationData, transition: buttonTransition) + + if wasAdded { + buttonNode.alpha = 0.0 + } + transition.updateAlpha(node: buttonNode, alpha: 1.0) + actionButtonRightOrigin.x -= actionButtonSize.width + buttonSpacing + } + + for key in self.actionButtonNodes.keys { + if !actionButtonKeys.contains(key) { + if let buttonNode = self.actionButtonNodes[key] { + self.actionButtonNodes.removeValue(forKey: key) + transition.updateAlpha(node: buttonNode, alpha: 0.0) { [weak buttonNode] _ in + buttonNode?.removeFromSupernode() + } + } + } + } + + 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) + if !actionButtonKeys.isEmpty { + buttonRightOrigin.y += actionButtonSize.height + 24.0 + } + + for buttonKey in buttonKeys.reversed() { + let buttonNode: PeerInfoHeaderButtonNode + var wasAdded = false + if let current = self.buttonNodes[buttonKey] { + buttonNode = current + } else { + wasAdded = true + buttonNode = PeerInfoHeaderButtonNode(key: buttonKey, action: { [weak self] buttonNode, gesture in + self?.buttonPressed(buttonNode, gesture: gesture) + }) + self.buttonNodes[buttonKey] = buttonNode + self.buttonsContainerNode.addSubnode(buttonNode) + } + + let buttonFrame = CGRect(origin: CGPoint(x: buttonRightOrigin.x - buttonSize.width, y: buttonRightOrigin.y), size: buttonSize) + let buttonTransition: ContainedViewLayoutTransition = wasAdded ? .immediate : transition + + if additive { + buttonTransition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) + } else { + buttonTransition.updateFrame(node: buttonNode, frame: buttonFrame) + } + let buttonText: String + let buttonIcon: PeerInfoHeaderButtonIcon + switch buttonKey { + case .message: + buttonText = presentationData.strings.PeerInfo_ButtonMessage + buttonIcon = .message + case .discussion: + buttonText = presentationData.strings.PeerInfo_ButtonDiscuss + buttonIcon = .message + case .call: + buttonText = presentationData.strings.PeerInfo_ButtonCall + buttonIcon = .call + case .videoCall: + buttonText = presentationData.strings.PeerInfo_ButtonVideoCall + buttonIcon = .videoCall + case .voiceChat: + if let channel = peer as? TelegramChannel, case .broadcast = channel.info { + buttonText = presentationData.strings.PeerInfo_ButtonLiveStream + } else { + buttonText = presentationData.strings.PeerInfo_ButtonVoiceChat + } + buttonIcon = .voiceChat + case .mute: + let chatIsMuted = peerInfoIsChatMuted(peer: peer, peerNotificationSettings: peerNotificationSettings, threadNotificationSettings: threadNotificationSettings, globalNotificationSettings: globalNotificationSettings) + if chatIsMuted { + buttonText = presentationData.strings.PeerInfo_ButtonUnmute + buttonIcon = .unmute + } else { + buttonText = presentationData.strings.PeerInfo_ButtonMute + buttonIcon = .mute + } + case .more: + buttonText = presentationData.strings.PeerInfo_ButtonMore + buttonIcon = .more + case .addMember: + buttonText = presentationData.strings.PeerInfo_ButtonAddMember + buttonIcon = .addMember + case .search: + buttonText = presentationData.strings.PeerInfo_ButtonSearch + buttonIcon = .search + case .leave: + buttonText = presentationData.strings.PeerInfo_ButtonLeave + buttonIcon = .leave + case .stop: + buttonText = presentationData.strings.PeerInfo_ButtonStop + buttonIcon = .stop + case .addContact: + fatalError() + } + + var isActive = true + if let highlightedButton = state.highlightedButton { + isActive = buttonKey == highlightedButton + } + + buttonNode.update(size: buttonFrame.size, text: buttonText, icon: buttonIcon, isActive: isActive, presentationData: presentationData, backgroundColor: contentButtonBackgroundColor, foregroundColor: contentButtonForegroundColor, fraction: 1.0 - innerButtonsTransitionFraction, transition: buttonTransition) + + if wasAdded { + buttonNode.alpha = 0.0 + } + transition.updateAlpha(node: buttonNode, alpha: buttonsTransitionFraction) + + if case .mute = buttonKey, buttonNode.containerNode.alpha.isZero, additive { + if case let .animated(duration, curve) = transition { + ContainedViewLayoutTransition.animated(duration: duration * 0.3, curve: curve).updateAlpha(node: buttonNode.containerNode, alpha: 1.0) + } else { + transition.updateAlpha(node: buttonNode.containerNode, alpha: 1.0) + } + } else { + transition.updateAlpha(node: buttonNode.containerNode, alpha: 1.0) + } + buttonRightOrigin.x -= buttonSize.width + buttonSpacing + } + + for key in self.buttonNodes.keys { + if !buttonKeys.contains(key) { + if let buttonNode = self.buttonNodes[key] { + self.buttonNodes.removeValue(forKey: key) + transition.updateAlpha(node: buttonNode, alpha: 0.0) { [weak buttonNode] _ in + buttonNode?.removeFromSupernode() + } + } + } + } + + let resolvedRegularHeight: CGFloat + if self.isAvatarExpanded { + resolvedRegularHeight = expandedAvatarListSize.height + } else { + resolvedRegularHeight = panelWithAvatarHeight + navigationHeight + } + + let backgroundFrame: CGRect + let separatorFrame: CGRect + + var resolvedHeight: CGFloat + + if state.isEditing { + resolvedHeight = editingContentHeight + backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -2000.0 + max(navigationHeight, resolvedHeight - contentOffset)), size: CGSize(width: width, height: 2000.0)) + separatorFrame = CGRect(origin: CGPoint(x: 0.0, y: max(navigationHeight, resolvedHeight - contentOffset)), size: CGSize(width: width, height: UIScreenPixel)) + } else { + resolvedHeight = resolvedRegularHeight + backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -2000.0 + apparentHeight), size: CGSize(width: width, height: 2000.0)) + separatorFrame = CGRect(origin: CGPoint(x: 0.0, y: apparentHeight), size: CGSize(width: width, height: UIScreenPixel)) + } + + transition.updateFrame(node: self.regularContentNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: resolvedHeight))) + transition.updateFrameAdditive(node: self.buttonsContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: apparentBackgroundHeight - backgroundHeight), size: CGSize(width: width, height: 1000.0))) + + navigationTransition.updateAlpha(node: self.buttonsContainerNode, alpha: backgroundBannerAlpha) + + let bannerFrame = CGRect(origin: CGPoint(x: 0.0, y: -2000.0 + apparentBackgroundHeight), size: CGSize(width: width, height: 2000.0)) + + if additive { + transition.updateFrameAdditive(view: self.backgroundBannerView, frame: bannerFrame) + } else { + transition.updateFrame(view: self.backgroundBannerView, frame: bannerFrame) + } + + let backgroundCoverSize = self.backgroundCover.update( + transition: Transition(transition), + component: AnyComponent(PeerInfoCoverComponent( + context: self.context, + peer: peer.flatMap(EnginePeer.init), + files: [:], + isDark: presentationData.theme.overallDarkAppearance, + avatarCenter: apparentAvatarFrame.center, + avatarScale: avatarScale, + defaultHeight: 254.0, + avatarTransitionFraction: max(0.0, min(1.0, titleCollapseFraction + transitionFraction * 2.0)), + patternTransitionFraction: buttonsTransitionFraction * backgroundTransitionFraction + )), + environment: {}, + containerSize: CGSize(width: width, height: apparentBackgroundHeight) + ) + if let backgroundCoverView = self.backgroundCover.view { + if backgroundCoverView.superview == nil { + self.backgroundBannerView.addSubview(backgroundCoverView) + } + if additive { + transition.updateFrameAdditive(view: backgroundCoverView, frame: CGRect(origin: CGPoint(x: 0.0, y: bannerFrame.height - backgroundCoverSize.height), size: backgroundCoverSize)) + } else { + transition.updateFrame(view: backgroundCoverView, frame: CGRect(origin: CGPoint(x: 0.0, y: bannerFrame.height - backgroundCoverSize.height), size: backgroundCoverSize)) + } + } + + if additive { + transition.updateFrameAdditive(node: self.backgroundNode, frame: backgroundFrame) + self.backgroundNode.update(size: self.backgroundNode.bounds.size, transition: transition) + transition.updateFrameAdditive(node: self.expandedBackgroundNode, frame: backgroundFrame) + self.expandedBackgroundNode.update(size: self.expandedBackgroundNode.bounds.size, transition: transition) + transition.updateFrameAdditive(node: self.separatorNode, frame: separatorFrame) + } else { + transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) + self.backgroundNode.update(size: self.backgroundNode.bounds.size, transition: transition) + transition.updateFrame(node: self.expandedBackgroundNode, frame: backgroundFrame) + self.expandedBackgroundNode.update(size: self.expandedBackgroundNode.bounds.size, transition: transition) + transition.updateFrame(node: self.separatorNode, frame: separatorFrame) + } + + if !state.isEditing { + if !isSettings { + if self.isAvatarExpanded { + resolvedHeight -= 21.0 + } else { + resolvedHeight += 79.0 + + if !actionButtonKeys.isEmpty { + resolvedHeight += 64.0 + } + } + } else { + if self.isAvatarExpanded { + resolvedHeight -= 21.0 + } + } + } + + if isFirstTime { + self.updateAvatarMask(transition: .immediate) + } + + return resolvedHeight + } + + private func buttonPressed(_ buttonNode: PeerInfoHeaderButtonNode, gesture: ContextGesture?) { + self.performButtonAction?(buttonNode.key, gesture) + } + + private func actionButtonPressed(_ buttonNode: PeerInfoHeaderActionButtonNode, gesture: ContextGesture?) { + self.performButtonAction?(buttonNode.key, gesture) + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + guard let result = super.hitTest(point, with: event) else { + return nil + } + if !self.backgroundNode.frame.contains(point) { + return nil + } + + let setByFrame = self.avatarListNode.listContainerNode.setByYouNode.view.convert(self.avatarListNode.listContainerNode.setByYouNode.bounds, to: self.view).insetBy(dx: -44.0, dy: 0.0) + if self.avatarListNode.listContainerNode.setByYouNode.alpha > 0.0, setByFrame.contains(point) { + return self.avatarListNode.listContainerNode.setByYouNode.view + } + + if !(self.state?.isEditing ?? false) { + switch self.currentCredibilityIcon { + case .premium, .emojiStatus: + let iconFrame = self.titleCredibilityIconView.convert(self.titleCredibilityIconView.bounds, to: self.view) + let expandedIconFrame = self.titleExpandedCredibilityIconView.convert(self.titleExpandedCredibilityIconView.bounds, to: self.view) + if expandedIconFrame.contains(point) && self.isAvatarExpanded { + return self.titleExpandedCredibilityIconView.hitTest(self.view.convert(point, to: self.titleExpandedCredibilityIconView), with: event) + } else if iconFrame.contains(point) { + return self.titleCredibilityIconView.hitTest(self.view.convert(point, to: self.titleCredibilityIconView), with: event) + } + default: + break + } + } + + if let subtitleBackgroundButton = self.subtitleBackgroundButton, subtitleBackgroundButton.view.convert(subtitleBackgroundButton.bounds, to: self.view).contains(point) { + if let result = subtitleBackgroundButton.view.hitTest(self.view.convert(point, to: subtitleBackgroundButton.view), with: event) { + return result + } + } + + if result.isDescendant(of: self.navigationButtonContainer.view) { + return result + } + + if let result = self.buttonsContainerNode.view.hitTest(self.view.convert(point, to: self.buttonsContainerNode.view), with: event) { + return result + } + + if result == self.view || result == self.regularContentNode.view || result == self.editingContentNode.view { + return nil + } + + return result + } + + func updateIsAvatarExpanded(_ isAvatarExpanded: Bool, transition: ContainedViewLayoutTransition) { + if self.isAvatarExpanded != isAvatarExpanded { + self.isAvatarExpanded = isAvatarExpanded + if isAvatarExpanded { + self.avatarListNode.listContainerNode.selectFirstItem() + } + if case .animated = transition, !isAvatarExpanded { + self.avatarListNode.animateAvatarCollapse(transition: transition) + } + + self.updateAvatarMask(transition: transition) + } + } + + private func updateAvatarMask(transition: ContainedViewLayoutTransition) { + guard let (width, deviceMetrics) = self.validLayout, deviceMetrics.hasDynamicIsland else { + return + } + let maskScale: CGFloat = isAvatarExpanded ? width / 100.0 : 1.0 + transition.updateTransformScale(layer: self.avatarListNode.maskNode.layer, scale: maskScale) + transition.updateTransformScale(layer: self.avatarListNode.bottomCoverNode.layer, scale: maskScale) + transition.updateTransformScale(layer: self.avatarListNode.topCoverNode.layer, scale: maskScale) + + let maskAnchorPoint = CGPoint(x: 0.5, y: isAvatarExpanded ? 0.37 : 0.5) + transition.updateAnchorPoint(layer: self.avatarListNode.maskNode.layer, anchorPoint: maskAnchorPoint) + } +} + diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderSingleLineTextFieldNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderSingleLineTextFieldNode.swift new file mode 100644 index 00000000000..95638a8cd38 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderSingleLineTextFieldNode.swift @@ -0,0 +1,151 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import ContextUI +import TelegramPresentationData +import Display + +final class PeerInfoHeaderSingleLineTextFieldNode: ASDisplayNode, PeerInfoHeaderTextFieldNode, UITextFieldDelegate { + private let backgroundNode: ASDisplayNode + private let textNode: TextFieldNode + private let measureTextNode: ImmediateTextNode + private let clearIconNode: ASImageNode + private let clearButtonNode: HighlightableButtonNode + private let topSeparator: ASDisplayNode + private let maskNode: ASImageNode + + private var theme: PresentationTheme? + + var text: String { + return self.textNode.textField.text ?? "" + } + + override init() { + self.backgroundNode = ASDisplayNode() + + self.textNode = TextFieldNode() + self.measureTextNode = ImmediateTextNode() + self.measureTextNode.maximumNumberOfLines = 0 + + self.clearIconNode = ASImageNode() + self.clearIconNode.isLayerBacked = true + self.clearIconNode.displayWithoutProcessing = true + self.clearIconNode.displaysAsynchronously = false + self.clearIconNode.isHidden = true + + self.clearButtonNode = HighlightableButtonNode() + self.clearButtonNode.isHidden = true + self.clearButtonNode.isAccessibilityElement = false + + self.topSeparator = ASDisplayNode() + + self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false + + super.init() + + self.addSubnode(self.backgroundNode) + self.addSubnode(self.textNode) + self.addSubnode(self.clearIconNode) + self.addSubnode(self.clearButtonNode) + self.addSubnode(self.topSeparator) + self.addSubnode(self.maskNode) + + self.textNode.textField.delegate = self + + self.clearButtonNode.addTarget(self, action: #selector(self.clearButtonPressed), forControlEvents: .touchUpInside) + self.clearButtonNode.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + strongSelf.clearIconNode.layer.removeAnimation(forKey: "opacity") + strongSelf.clearIconNode.alpha = 0.4 + } else { + strongSelf.clearIconNode.alpha = 1.0 + strongSelf.clearIconNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + } + } + } + } + + @objc private func clearButtonPressed() { + self.textNode.textField.text = "" + self.updateClearButtonVisibility() + } + + @objc func textFieldDidBeginEditing(_ textField: UITextField) { + self.updateClearButtonVisibility() + } + + @objc func textFieldDidEndEditing(_ textField: UITextField) { + self.updateClearButtonVisibility() + } + + private func updateClearButtonVisibility() { + let isHidden = !self.textNode.textField.isFirstResponder || self.text.isEmpty + self.clearIconNode.isHidden = isHidden + self.clearButtonNode.isHidden = isHidden + self.clearButtonNode.isAccessibilityElement = isHidden + } + + func update(width: CGFloat, safeInset: CGFloat, isSettings: Bool, hasPrevious: Bool, hasNext: Bool, placeholder: String, isEnabled: Bool, presentationData: PresentationData, updateText: String?) -> CGFloat { + let titleFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) + self.textNode.textField.font = titleFont + + if self.theme !== presentationData.theme { + self.theme = presentationData.theme + + self.backgroundNode.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor + + self.textNode.textField.textColor = presentationData.theme.list.itemPrimaryTextColor + self.textNode.textField.keyboardAppearance = presentationData.theme.rootController.keyboardColor.keyboardAppearance + self.textNode.textField.tintColor = presentationData.theme.list.itemAccentColor + + self.clearIconNode.image = PresentationResourcesItemList.itemListClearInputIcon(presentationData.theme) + } + + let attributedPlaceholderText = NSAttributedString(string: placeholder, font: titleFont, textColor: presentationData.theme.list.itemPlaceholderTextColor) + if self.textNode.textField.attributedPlaceholder == nil || !self.textNode.textField.attributedPlaceholder!.isEqual(to: attributedPlaceholderText) { + self.textNode.textField.attributedPlaceholder = attributedPlaceholderText + self.textNode.textField.accessibilityHint = attributedPlaceholderText.string + } + + if let updateText = updateText { + self.textNode.textField.text = updateText + } + + if !hasPrevious { + self.topSeparator.isHidden = true + } + self.topSeparator.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor + let separatorX = safeInset + (hasPrevious ? 16.0 : 0.0) + self.topSeparator.frame = CGRect(origin: CGPoint(x: separatorX, y: 0.0), size: CGSize(width: width - separatorX - safeInset, height: UIScreenPixel)) + + let measureText = "|" + let attributedMeasureText = NSAttributedString(string: measureText, font: titleFont, textColor: .black) + self.measureTextNode.attributedText = attributedMeasureText + let measureTextSize = self.measureTextNode.updateLayout(CGSize(width: width - safeInset * 2.0 - 16.0 * 2.0 - 38.0, height: .greatestFiniteMagnitude)) + + let height = measureTextSize.height + 22.0 + + let buttonSize = CGSize(width: 38.0, height: height) + self.clearButtonNode.frame = CGRect(origin: CGPoint(x: width - safeInset - buttonSize.width, y: 0.0), size: buttonSize) + if let image = self.clearIconNode.image { + self.clearIconNode.frame = CGRect(origin: CGPoint(x: width - safeInset - buttonSize.width + floor((buttonSize.width - image.size.width) / 2.0), y: floor((height - image.size.height) / 2.0)), size: image.size) + } + + self.backgroundNode.frame = CGRect(origin: CGPoint(x: safeInset, y: 0.0), size: CGSize(width: max(1.0, width - safeInset * 2.0), height: height)) + self.textNode.frame = CGRect(origin: CGPoint(x: safeInset + 16.0, y: floor((height - 40.0) / 2.0)), size: CGSize(width: max(1.0, width - safeInset * 2.0 - 16.0 * 2.0 - 38.0), height: 40.0)) + + let hasCorners = safeInset > 0.0 && (!hasPrevious || !hasNext) + let hasTopCorners = hasCorners && !hasPrevious + let hasBottomCorners = hasCorners && !hasNext + + self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + self.maskNode.frame = CGRect(origin: CGPoint(x: safeInset, y: 0.0), size: CGSize(width: width - safeInset - safeInset, height: height)) + + self.textNode.isUserInteractionEnabled = isEnabled + self.textNode.alpha = isEnabled ? 1.0 : 0.6 + + return height + } +} diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoMembers.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoMembers.swift similarity index 100% rename from submodules/TelegramUI/Sources/PeerInfo/PeerInfoMembers.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoMembers.swift diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoPaneContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift similarity index 98% rename from submodules/TelegramUI/Sources/PeerInfo/PeerInfoPaneContainerNode.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift index a375e520c7b..6600a16ca00 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoPaneContainerNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift @@ -352,7 +352,7 @@ private final class PeerInfoPendingPane { updatedPresentationData: (initial: PresentationData, signal: Signal)?, chatControllerInteraction: ChatControllerInteraction, data: PeerInfoScreenData, - openPeerContextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void, + openPeerContextAction: @escaping (Bool, Peer, ASDisplayNode, ContextGesture?) -> Void, openAddMemberAction: @escaping () -> Void, requestPerformPeerMemberAction: @escaping (PeerInfoMember, PeerMembersListAction) -> Void, peerId: PeerId, @@ -416,6 +416,8 @@ private final class PeerInfoPendingPane { } else { preconditionFailure() } + case .recommended: + paneNode = PeerInfoRecommendedChannelsPaneNode(context: context, peerId: peerId, chatControllerInteraction: chatControllerInteraction, openPeerContextAction: openPeerContextAction) } paneNode.parentController = parentController self.pane = PeerInfoPaneWrapper(key: key, node: paneNode) @@ -481,7 +483,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat var selectionPanelNode: PeerInfoSelectionPanelNode? var chatControllerInteraction: ChatControllerInteraction? - var openPeerContextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)? + var openPeerContextAction: ((Bool, Peer, ASDisplayNode, ContextGesture?) -> Void)? var openAddMemberAction: (() -> Void)? var requestPerformPeerMemberAction: ((PeerInfoMember, PeerMembersListAction) -> Void)? @@ -496,13 +498,16 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat private var currentAvailablePanes: [PeerInfoPaneKey]? private let updatedPresentationData: (initial: PresentationData, signal: Signal)? - init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, peerId: PeerId, chatLocation: ChatLocation, chatLocationContextHolder: Atomic, isMediaOnly: Bool) { + private let initialPaneKey: PeerInfoPaneKey? + + init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, peerId: PeerId, chatLocation: ChatLocation, chatLocationContextHolder: Atomic, isMediaOnly: Bool, initialPaneKey: PeerInfoPaneKey?) { self.context = context self.updatedPresentationData = updatedPresentationData self.peerId = peerId self.chatLocation = chatLocation self.chatLocationContextHolder = chatLocationContextHolder self.isMediaOnly = isMediaOnly + self.initialPaneKey = initialPaneKey self.additionalBackgroundNode = ASDisplayNode() @@ -728,7 +733,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat self.pendingSwitchToPaneKey = nil } } else if self.currentPaneKey == nil { - self.pendingSwitchToPaneKey = availablePanes.first + self.pendingSwitchToPaneKey = self.initialPaneKey ?? availablePanes.first } let currentIndex: Int? @@ -783,15 +788,15 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat } for key in requiredPendingKeys { - if self.pendingPanes[key] == nil { + if self.pendingPanes[key] == nil, let data { var leftScope = false let pane = PeerInfoPendingPane( context: self.context, updatedPresentationData: self.updatedPresentationData, chatControllerInteraction: self.chatControllerInteraction!, - data: data!, - openPeerContextAction: { [weak self] peer, node, gesture in - self?.openPeerContextAction?(peer, node, gesture) + data: data, + openPeerContextAction: { [weak self] recommended, peer, node, gesture in + self?.openPeerContextAction?(recommended, peer, node, gesture) }, openAddMemberAction: { [weak self] in self?.openAddMemberAction?() @@ -989,6 +994,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat title = presentationData.strings.PeerInfo_PaneGroups case .members: title = presentationData.strings.PeerInfo_PaneMembers + case .recommended: + title = presentationData.strings.PeerInfo_PaneRecommended } return PeerInfoPaneSpecifier(key: key, title: title) }, selectedPane: self.currentPaneKey, transitionFraction: self.transitionFraction, transition: transition) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift similarity index 97% rename from submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 7d48eff94ac..93abdc8731c 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -53,6 +53,7 @@ import NGAiChatUI import NGWebUtils import NGStrings import NGUI +import NGCore import NGData import NGEnv import NGLab @@ -106,8 +107,11 @@ import WebUI import ShareWithPeersScreen import ItemListPeerItem import PeerNameColorScreen +import PeerAllowedReactionsScreen +import ChatMessageSelectionInputPanelNode +import ChatHistorySearchContainerNode -enum PeerInfoAvatarEditingMode { +public enum PeerInfoAvatarEditingMode { case generic case accept case suggest @@ -579,6 +583,7 @@ private final class PeerInfoInteraction { let displayTopicsLimited: (TopicsLimitedReason) -> Void let openPeerMention: (String, ChatControllerInteractionNavigateToPeer) -> Void let openBotApp: (AttachMenuBot) -> Void + let openEditing: () -> Void init( getPeerRegDate: @escaping (Int64, Int64) -> Void, @@ -633,7 +638,8 @@ private final class PeerInfoInteraction { toggleForumTopics: @escaping (Bool) -> Void, displayTopicsLimited: @escaping (TopicsLimitedReason) -> Void, openPeerMention: @escaping (String, ChatControllerInteractionNavigateToPeer) -> Void, - openBotApp: @escaping (AttachMenuBot) -> Void + openBotApp: @escaping (AttachMenuBot) -> Void, + openEditing: @escaping () -> Void ) { self.getPeerRegDate = getPeerRegDate self.openUsername = openUsername @@ -688,6 +694,7 @@ private final class PeerInfoInteraction { self.displayTopicsLimited = displayTopicsLimited self.openPeerMention = openPeerMention self.openBotApp = openBotApp + self.openEditing = openEditing } } @@ -1052,6 +1059,7 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat let ItemAddAccount = 5 let ItemAddAccountHelp = 6 let ItemLogout = 7 + let ItemPeerColor = 8 items[.help]!.append(PeerInfoScreenCommentItem(id: ItemNameHelp, text: presentationData.strings.EditProfile_NameAndPhotoOrVideoHelp)) @@ -1077,6 +1085,21 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat interaction.openSettings(.username) })) + if let peer = data.peer as? TelegramUser { + var colors: [PeerNameColors.Colors] = [] + if let nameColor = peer.nameColor.flatMap({ context.peerNameColors.get($0, dark: presentationData.theme.overallDarkAppearance) }) { + colors.append(nameColor) + } + if let profileColor = peer.profileColor.flatMap({ context.peerNameColors.getProfile($0, dark: presentationData.theme.overallDarkAppearance, subject: .palette) }) { + colors.append(profileColor) + } + let colorImage = generateSettingsMenuPeerColorsLabelIcon(colors: colors) + + items[.info]!.append(PeerInfoScreenDisclosureItem(id: ItemPeerColor, label: .image(colorImage, colorImage.size), text: presentationData.strings.Settings_YourColor, icon: nil, action: { + interaction.editingOpenNameColorSetup() + })) + } + items[.account]!.append(PeerInfoScreenActionItem(id: ItemAddAccount, text: presentationData.strings.Settings_AddAnotherAccount, alignment: .center, action: { interaction.openSettings(.addAccount) })) @@ -1293,6 +1316,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese let ItemAdmins = 6 let ItemMembers = 7 let ItemMemberRequests = 8 + let ItemEdit = 9 if let _ = data.threadData { let mainUsername: String @@ -1443,6 +1467,10 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese interaction.openParticipantsSection(.memberRequests) })) } + + items[.peerMembers]!.append(PeerInfoScreenDisclosureItem(id: ItemEdit, label: .none, text: presentationData.strings.Channel_Info_Settings, icon: UIImage(bundleImageName: "Chat/Info/SettingsIcon"), action: { + interaction.openEditing() + })) } } } @@ -1749,7 +1777,11 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL } else { label = "" } - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { + var additionalBadgeLabel: String? = nil + if case .broadcast = channel.info { + additionalBadgeLabel = presentationData.strings.Settings_New + } + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), additionalBadgeLabel: additionalBadgeLabel, text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { interaction.editingOpenReactionsSetup() })) } @@ -2242,6 +2274,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro let isSettings: Bool private let isMediaOnly: Bool + let initialExpandPanes: Bool private var presentationData: PresentationData @@ -2303,6 +2336,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro private let addMemberDisposable = MetaDisposable() private let preloadHistoryDisposable = MetaDisposable() private var shareStatusDisposable: MetaDisposable? + private let joinChannelDisposable = MetaDisposable() private let editAvatarDisposable = MetaDisposable() private let updateAvatarDisposable = MetaDisposable() @@ -2348,7 +2382,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } private var didSetReady = false - init(controller: PeerInfoScreenImpl, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool, hintGroupInCommon: PeerId?, requestsContext: PeerInvitationImportersContext?, chatLocation: ChatLocation, chatLocationContextHolder: Atomic) { + init(controller: PeerInfoScreenImpl, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool, hintGroupInCommon: PeerId?, requestsContext: PeerInvitationImportersContext?, chatLocation: ChatLocation, chatLocationContextHolder: Atomic, initialPaneKey: PeerInfoPaneKey?) { self.controller = controller self.context = context self.peerId = peerId @@ -2362,6 +2396,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self.chatLocation = chatLocation self.chatLocationContextHolder = chatLocationContextHolder self.isMediaOnly = context.account.peerId == peerId && !isSettings + self.initialExpandPanes = initialPaneKey != nil self.scrollNode = ASScrollNode() self.scrollNode.view.delaysContentTouches = false @@ -2371,8 +2406,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if case let .replyThread(message) = chatLocation { forumTopicThreadId = Int64(message.messageId.id) } - self.headerNode = PeerInfoHeaderNode(context: context, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, isMediaOnly: self.isMediaOnly, isSettings: isSettings, forumTopicThreadId: forumTopicThreadId, chatLocation: self.chatLocation) - self.paneContainerNode = PeerInfoPaneContainerNode(context: context, updatedPresentationData: controller.updatedPresentationData, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, isMediaOnly: self.isMediaOnly) + self.headerNode = PeerInfoHeaderNode(context: context, controller: controller, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, isMediaOnly: self.isMediaOnly, isSettings: isSettings, forumTopicThreadId: forumTopicThreadId, chatLocation: self.chatLocation) + self.paneContainerNode = PeerInfoPaneContainerNode(context: context, updatedPresentationData: controller.updatedPresentationData, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, isMediaOnly: self.isMediaOnly, initialPaneKey: initialPaneKey) super.init() @@ -2552,6 +2587,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }, openBotApp: { [weak self] bot in self?.openBotApp(bot) + }, + openEditing: { [weak self] in + self?.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil) } ) @@ -2584,7 +2622,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } let gesture: ContextGesture? = anyRecognizer as? ContextGesture - let _ = (chatAvailableMessageActionsImpl(engine: strongSelf.context.engine, accountPeerId: strongSelf.context.account.peerId, messageIds: [message.id]) + let _ = (strongSelf.context.sharedContext.chatAvailableMessageActions(engine: strongSelf.context.engine, accountPeerId: strongSelf.context.account.peerId, messageIds: [message.id]) |> deliverOnMainQueue).startStandalone(next: { actions in guard let strongSelf = self else { return @@ -2723,7 +2761,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro c.dismiss(completion: { if let strongSelf = self { strongSelf.chatInterfaceInteraction.toggleMessagesSelection([message.id], true) - strongSelf.expandTabs() + strongSelf.expandTabs(animated: true) } }) }))) @@ -2750,7 +2788,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if let previewData = previewData { let context = strongSelf.context let strings = strongSelf.presentationData.strings - let items = chatAvailableMessageActionsImpl(engine: strongSelf.context.engine, accountPeerId: strongSelf.context.account.peerId, messageIds: [message.id]) + let items = strongSelf.context.sharedContext.chatAvailableMessageActions(engine: strongSelf.context.engine, accountPeerId: strongSelf.context.account.peerId, messageIds: [message.id]) |> map { actions -> [ContextMenuItem] in var items: [ContextMenuItem] = [] @@ -2875,7 +2913,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro return } strongSelf.chatInterfaceInteraction.toggleMessagesSelection([message.id], true) - strongSelf.expandTabs() + strongSelf.expandTabs(animated: true) f(.default) }))) @@ -3072,6 +3110,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }, openNoAdsDemo: { }, displayGiveawayParticipationStatus: { _ in }, openPremiumStatusInfo: { _, _, _, _ in + }, openRecommendedChannelContextMenu: { _, _, _ in }, requestMessageUpdate: { _, _ in }, cancelInteractiveKeyboardGestures: { }, dismissTextInput: { @@ -3106,27 +3145,42 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self.addSubnode(self.scrollNode) self.scrollNode.addSubnode(self.paneContainerNode) - if !self.isMediaOnly { - self.addSubnode(self.headerNode.buttonsContainerNode) - } self.addSubnode(self.headerNode) self.scrollNode.view.isScrollEnabled = !self.isMediaOnly self.paneContainerNode.chatControllerInteraction = self.chatInterfaceInteraction - self.paneContainerNode.openPeerContextAction = { [weak self] peer, node, gesture in + self.paneContainerNode.openPeerContextAction = { [weak self] recommended, peer, node, gesture in guard let strongSelf = self, let controller = strongSelf.controller else { return } let presentationData = strongSelf.presentationData let chatController = strongSelf.context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) - let items: [ContextMenuItem] = [ - .action(ContextMenuActionItem(text: presentationData.strings.Conversation_LinkDialogOpen, icon: { _ in nil }, action: { _, f in - f(.dismissWithoutContent) - self?.chatInterfaceInteraction.openPeer(EnginePeer(peer), .default, nil, .default) - })) - ] - let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let items: [ContextMenuItem] + if recommended { + items = [ + .action(ContextMenuActionItem(text: presentationData.strings.Conversation_LinkDialogOpen, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ImageEnlarge"), color: theme.actionSheet.primaryTextColor) }, action: { [weak self] _, f in + f(.dismissWithoutContent) + self?.chatInterfaceInteraction.openPeer(EnginePeer(peer), .default, nil, .default) + })), + .action(ContextMenuActionItem(text: presentationData.strings.Chat_SimilarChannels_Join, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.actionSheet.primaryTextColor) }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + guard let self else { + return + } + self.joinChannel(peer: EnginePeer(peer)) + })) + ] + } else { + items = [ + .action(ContextMenuActionItem(text: presentationData.strings.Conversation_LinkDialogOpen, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ImageEnlarge"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in + f(.dismissWithoutContent) + self?.chatInterfaceInteraction.openPeer(EnginePeer(peer), .default, nil, .default) + })) + ] + } + let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, passthroughTouches: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) controller.presentInGlobalOverlay(contextController) } @@ -3400,6 +3454,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro return } switch key { + case .back: + strongSelf.controller?.dismiss() case .edit: if case let .replyThread(message) = strongSelf.chatLocation { let threadId = Int64(message.messageId.id) @@ -3452,7 +3508,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } }) - strongSelf.controller?.navigationItem.setLeftBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, style: .plain, target: strongSelf, action: #selector(strongSelf.editingCancelPressed)), animated: true) } case .done, .cancel: strongSelf.view.endEditing(true) @@ -3768,7 +3823,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } UIView.transition(with: strongSelf.view, duration: 0.3, options: [.transitionCrossDissolve], animations: { }, completion: nil) - strongSelf.controller?.navigationItem.setLeftBarButton(nil, animated: true) } (strongSelf.controller?.parent as? TabBarController)?.updateIsTabBarHidden(false, transition: .animated(duration: 0.3, curve: .linear)) case .select: @@ -4068,7 +4122,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro // self.headerNode.avatarListNode.listContainerNode.currentIndexUpdated = { [weak self] in - self?.updateNavigation(transition: .immediate, additive: true) + self?.updateNavigation(transition: .immediate, additive: true, animateHeader: true) } self.dataDisposable = combineLatest( @@ -4112,17 +4166,23 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro |> deliverOnMainQueue).startStrict(next: { [weak self] translationState in self?.translationState = translationState }) + + let _ = context.engine.peers.requestRecommendedChannels(peerId: peerId, forceUpdate: true).startStandalone() } if peerId.namespace == Namespaces.Peer.CloudChannel || peerId.namespace == Namespaces.Peer.CloudUser { self.storiesReady.set(false) let expiringStoryList = PeerExpiringStoryListContext(account: context.account, peerId: peerId) self.expiringStoryList = expiringStoryList - self.storyUploadProgressDisposable = (context.engine.messages.allStoriesUploadProgress() - |> map { value -> Float? in - return value[peerId] - } - |> distinctUntilChanged).startStrict(next: { [weak self] value in + self.storyUploadProgressDisposable = ( + combineLatest(context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) + |> distinctUntilChanged, + context.engine.messages.allStoriesUploadProgress() + |> map { value -> Float? in + return value[peerId] + } + |> distinctUntilChanged + )).startStrict(next: { [weak self] peer, value in guard let self else { return } @@ -4133,7 +4193,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if self.headerNode.avatarListNode.avatarContainerNode.storyProgress != mappedValue { self.headerNode.avatarListNode.avatarContainerNode.storyProgress = mappedValue - self.headerNode.avatarListNode.avatarContainerNode.updateStoryView(transition: .immediate, theme: self.presentationData.theme) + self.headerNode.avatarListNode.avatarContainerNode.updateStoryView(transition: .immediate, theme: self.presentationData.theme, peer: peer?._asPeer()) } }) self.expiringStoryListDisposable = (combineLatest(queue: .mainQueue(), @@ -4211,6 +4271,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self.postingAvailabilityDisposable?.dispose() self.storyUploadProgressDisposable?.dispose() self.updateAvatarDisposable.dispose() + self.joinChannelDisposable.dispose() } override func didLoad() { @@ -4351,7 +4412,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - private func expandTabs() { + func expandTabs(animated: Bool) { if self.headerNode.isAvatarExpanded { let transition: ContainedViewLayoutTransition = .animated(duration: 0.35, curve: .spring) @@ -4367,7 +4428,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro let contentOffset = self.scrollNode.view.contentOffset let paneAreaExpansionFinalPoint: CGFloat = self.paneContainerNode.frame.minY - navigationHeight if contentOffset.y < paneAreaExpansionFinalPoint - CGFloat.ulpOfOne { - self.scrollNode.view.setContentOffset(CGPoint(x: 0.0, y: paneAreaExpansionFinalPoint), animated: true) + self.scrollNode.view.setContentOffset(CGPoint(x: 0.0, y: paneAreaExpansionFinalPoint), animated: animated) } } } @@ -4669,7 +4730,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self?.controller?.present(c, in: .window(.root), with: a) }, dismissInput: { [weak self] in self?.view.endEditing(true) - }, contentContext: nil, progress: nil) + }, contentContext: nil, progress: nil, completion: nil) } private func openUrl(url: String, concealed: Bool, external: Bool, forceExternal: Bool = false, commit: @escaping () -> Void = {}) { @@ -4695,7 +4756,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self?.controller?.present(c, in: .window(.root), with: a) }, dismissInput: { self?.view.endEditing(true) - }, contentContext: nil, progress: nil) + }, contentContext: nil, progress: nil, completion: nil) }) } @@ -5617,7 +5678,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro return } self.controller?.view.endEditing(true) - }, contentContext: nil, progress: nil) + }, contentContext: nil, progress: nil, completion: nil) }, action: nil as ((ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void)?))) c.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) @@ -5806,7 +5867,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro return } self.controller?.view.endEditing(true) - }, contentContext: nil, progress: nil) + }, contentContext: nil, progress: nil, completion: nil) }, action: nil as ((ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void)?))) c.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) @@ -5934,7 +5995,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro return } self.controller?.view.endEditing(true) - }, contentContext: nil, progress: nil) + }, contentContext: nil, progress: nil, completion: nil) }, action: nil as ((ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void)?))) c.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) @@ -6030,17 +6091,17 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro private func openChatWithMessageSearch() { if let navigationController = (self.controller?.navigationController as? NavigationController) { if case let .replyThread(currentMessage) = self.chatLocation, let current = navigationController.viewControllers.first(where: { controller in - if let controller = controller as? ChatControllerImpl, case let .replyThread(message) = controller.chatLocation, message.messageId == currentMessage.messageId { + if let controller = controller as? ChatController, case let .replyThread(message) = controller.chatLocation, message.messageId == currentMessage.messageId { return true } return false - }) as? ChatControllerImpl { + }) as? ChatController { var viewControllers = navigationController.viewControllers if let index = viewControllers.firstIndex(of: current) { viewControllers.removeSubrange(index + 1 ..< viewControllers.count) } navigationController.setViewControllers(viewControllers, animated: true) - current.activateSearch() + current.activateSearch(domain: .everything, query: "") } else if let peer = self.data?.chatPeer { self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: self.nearbyPeerDistance != nil ? .always : .default, activateMessageSearch: (.everything, ""), peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), completion: { [weak self] _ in if let strongSelf = self, strongSelf.nearbyPeerDistance != nil { @@ -6798,7 +6859,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: self.nearbyPeerDistance != nil ? .always : .default, peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), setupController: { controller in - (controller as? ChatControllerImpl)?.beginClearHistory(type: type) + controller.beginClearHistory(type: type) }, completion: { [weak self] _ in if let strongSelf = self, strongSelf.nearbyPeerDistance != nil { var viewControllers = navigationController.viewControllers @@ -6905,21 +6966,16 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } private func openStats(boosts: Bool = false, boostStatus: ChannelBoostStatus? = nil) { - guard let controller = self.controller, let data = self.data, let peer = data.peer, let cachedData = data.cachedData else { + guard let controller = self.controller, let data = self.data, let peer = data.peer else { return } self.view.endEditing(true) - var statsDatacenterId: Int32? - if let cachedData = cachedData as? CachedChannelData { - statsDatacenterId = cachedData.statsDatacenterId - } - let statsController: ViewController if let channel = peer as? TelegramChannel, case .group = channel.info { - statsController = groupStatsController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: peer.id, statsDatacenterId: statsDatacenterId) + statsController = groupStatsController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: peer.id) } else { - statsController = channelStatsController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: peer.id, section: boosts ? .boosts : .stats, boostStatus: boostStatus, statsDatacenterId: statsDatacenterId) + statsController = channelStatsController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: peer.id, section: boosts ? .boosts : .stats, boostStatus: boostStatus) } controller.push(statsController) } @@ -7255,7 +7311,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro controller?.present(c, in: .window(.root), with: a) }, dismissInput: { [weak controller] in controller?.view.endEditing(true) - }, contentContext: nil, progress: nil) + }, contentContext: nil, progress: nil, completion: nil) } private func performBotCommand(command: PeerInfoBotCommand) { @@ -7302,8 +7358,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } private func editingOpenNameColorSetup() { - let controller = PeerNameColorScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, subject: .channel(self.peerId)) - self.controller?.push(controller) + if self.peerId == self.context.account.peerId { + let controller = PeerNameColorScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, subject: .account) + self.controller?.push(controller) + } else { + let controller = PeerNameColorScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, subject: .channel(self.peerId)) + self.controller?.push(controller) + } } private func editingOpenInviteLinksSetup() { @@ -7321,7 +7382,18 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro guard let data = self.data, let peer = data.peer else { return } - self.controller?.push(peerAllowedReactionListController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: peer.id)) + if let channel = peer as? TelegramChannel, case .broadcast = channel.info { + let subscription = Promise() + subscription.set(PeerAllowedReactionsScreen.content(context: self.context, peerId: peer.id)) + let _ = (subscription.get() |> take(1) |> deliverOnMainQueue).start(next: { [weak self] content in + guard let self else { + return + } + self.controller?.push(PeerAllowedReactionsScreen(context: self.context, peerId: peer.id, initialContent: content)) + }) + } else { + self.controller?.push(peerAllowedReactionListController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: peer.id)) + } } private func toggleForumTopics(isEnabled: Bool) { @@ -8567,7 +8639,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if self.isSettings && self.data?.globalSettings?.privacySettings?.phoneDiscoveryEnabled == false && (self.data?.peer?.addressName ?? "").isEmpty { temporary = true } - controller.present(ChatQrCodeScreen(context: self.context, subject: .peer(peer: peer, threadId: threadId, temporary: temporary)), in: .window(.root)) + controller.present(self.context.sharedContext.makeChatQrCodeScreen(context: self.context, peer: peer, threadId: threadId, temporary: temporary), in: .window(.root)) } private func openPostStory() { @@ -8791,8 +8863,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro case .support: // MARK: Nicegram GermanSupport if self.presentationData.strings.baseLanguageCode.lowercased().contains("de"), - let url = URL(string: "ncg://resolve?domain=EinleitungHilfeTelegram") { - UIApplication.shared.open(url) + let url = URL(string: "https://t.me/EinleitungHilfeTelegram") { + CoreContainer.shared.urlOpener().open(url) break } // @@ -8948,7 +9020,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro strongSelf.context.sharedContext.openResolvedUrl(resolvedUrl, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.controller?.navigationController as? NavigationController, forceExternal: false, openPeer: { peer, navigation in }, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { [weak self] controller, arguments in self?.controller?.push(controller) - }, dismissInput: {}, contentContext: nil, progress: nil) + }, dismissInput: {}, contentContext: nil, progress: nil, completion: nil) } }) } @@ -9286,7 +9358,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro proceed(chatController) }) } else { - proceed(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(id: peerId))) + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .none, botStart: nil, mode: .standard(previewing: false)) + proceed(chatController) } } }) @@ -9337,7 +9410,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } else if let currentPaneKey = self.paneContainerNode.currentPaneKey, case .members = currentPaneKey { self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .list, placeholder: self.presentationData.strings.Common_Search, hasBackground: true, hasSeparator: true, contentNode: ChannelMembersSearchContainerNode(context: self.context, forceTheme: nil, peerId: self.peerId, mode: .searchMembers, filters: [], searchContext: self.groupMembersSearchContext, openPeer: { [weak self] peer, participant in - self?.openPeer(peerId: peer.id, navigation: .info) + self?.openPeer(peerId: peer.id, navigation: .info(nil)) }, updateActivity: { _ in }, pushController: { [weak self] c in self?.controller?.push(c) @@ -9789,7 +9862,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?.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, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: transition, additive: additive) + 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?.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, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: transition, additive: additive, animateHeader: transition.isAnimated) 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) @@ -10094,7 +10167,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } self.ignoreScrolling = false - self.updateNavigation(transition: transition, additive: additive) + self.updateNavigation(transition: transition, additive: additive, animateHeader: true) if !self.didSetReady && self.data != nil { self.didSetReady = true @@ -10114,7 +10187,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } private var hasQrButton = false - fileprivate func updateNavigation(transition: ContainedViewLayoutTransition, additive: Bool) { + fileprivate func updateNavigation(transition: ContainedViewLayoutTransition, additive: Bool, animateHeader: Bool) { let offsetY = self.scrollNode.view.contentOffset.y if self.isSettings, !(self.controller?.movingInHierarchy == true) { @@ -10152,7 +10225,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?.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, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: transition, additive: additive) + 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?.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, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: transition, additive: additive, animateHeader: animateHeader) } let paneAreaExpansionDistance: CGFloat = 32.0 @@ -10180,11 +10253,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self.paneContainerNode.update(size: self.paneContainerNode.bounds.size, sideInset: layout.safeInsets.left, bottomInset: bottomInset, visibleHeight: visibleHeight, expansionFraction: effectiveAreaExpansionFraction, presentationData: self.presentationData, data: self.data, transition: transition) transition.updateFrame(node: self.headerNode.navigationButtonContainer, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left, y: layout.statusBarHeight ?? 0.0), size: CGSize(width: layout.size.width - layout.safeInsets.left * 2.0, height: navigationBarHeight))) - self.headerNode.navigationButtonContainer.isWhite = self.headerNode.isAvatarExpanded + var leftNavigationButtons: [PeerInfoHeaderNavigationButtonSpec] = [] var rightNavigationButtons: [PeerInfoHeaderNavigationButtonSpec] = [] if self.state.isEditing { + leftNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .cancel, isForExpandedView: false)) rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .done, isForExpandedView: false)) } else { if self.isSettings { @@ -10220,6 +10294,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .selectionDone, isForExpandedView: true)) } } + if leftNavigationButtons.isEmpty, let controller = self.controller, let previousItem = controller.previousItem { + switch previousItem { + case .close, .item: + leftNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .back, isForExpandedView: false)) + } + } self.headerNode.navigationButtonContainer.update(size: CGSize(width: layout.size.width - layout.safeInsets.left * 2.0, height: navigationBarHeight), presentationData: self.presentationData, leftButtons: leftNavigationButtons, rightButtons: rightNavigationButtons, expandFraction: effectiveAreaExpansionFraction, transition: transition) } } @@ -10296,7 +10376,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - self.updateNavigation(transition: .immediate, additive: false) + self.updateNavigation(transition: .immediate, additive: false, animateHeader: true) } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { @@ -10323,9 +10403,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } private func updateNavigationExpansionPresentation(isExpanded: Bool, animated: Bool) { - if let controller = self.controller { - controller.setStatusBarStyle(isExpanded ? .White : self.presentationData.theme.rootController.statusBarStyle.style, animated: animated) - + /*if let controller = self.controller { if animated { UIView.transition(with: controller.controllerNode.headerNode.navigationButtonContainer.view, duration: 0.3, options: [.transitionCrossDissolve], animations: { }, completion: nil) @@ -10334,7 +10412,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro let baseNavigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData) let navigationBarPresentationData = NavigationBarPresentationData( theme: NavigationBarTheme( - buttonColor: isExpanded ? .white : baseNavigationBarPresentationData.theme.buttonColor, + buttonColor: .white, disabledButtonColor: baseNavigationBarPresentationData.theme.disabledButtonColor, primaryTextColor: baseNavigationBarPresentationData.theme.primaryTextColor, backgroundColor: .clear, @@ -10346,7 +10424,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro ), strings: baseNavigationBarPresentationData.strings) controller.setNavigationBarPresentationData(navigationBarPresentationData, animated: animated) - } + }*/ } func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { @@ -10426,6 +10504,43 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } return false } + + fileprivate func joinChannel(peer: EnginePeer) { + let presentationData = self.presentationData + self.joinChannelDisposable.set(( + self.context.peerChannelMemberCategoriesContextsManager.join(engine: self.context.engine, peerId: peer.id, hash: nil) + |> deliverOnMainQueue + |> afterCompleted { [weak self] in + Queue.mainQueue().async { + if let self { + self.controller?.present(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.Chat_SimilarChannels_JoinedChannel(peer.compactDisplayTitle).string, timeout: nil, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root)) + } + } + } + ).startStrict(error: { [weak self] error in + guard let self else { + return + } + let text: String + switch error { + case .inviteRequestSent: + self.controller?.present(UndoOverlayController(presentationData: presentationData, content: .inviteRequestSent(title: presentationData.strings.Group_RequestToJoinSent, text: presentationData.strings.Group_RequestToJoinSentDescriptionGroup), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root)) + return + case .tooMuchJoined: + self.controller?.push(oldChannelsController(context: context, intent: .join, completed: { [weak self] value in + if value { + self?.joinChannel(peer: peer) + } + })) + return + case .tooMuchUsers: + text = self.presentationData.strings.Conversation_UsersTooMuchError + case .generic: + text = self.presentationData.strings.Channel_ErrorAccessDenied + } + self.controller?.present(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + })) + } } public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortcutResponder { @@ -10440,10 +10555,11 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc private let isSettings: Bool private let hintGroupInCommon: PeerId? private weak var requestsContext: PeerInvitationImportersContext? + private let switchToRecommendedChannels: Bool private let chatLocation: ChatLocation private let chatLocationContextHolder = Atomic(value: nil) - weak var parentController: TelegramRootController? + public weak var parentController: TelegramRootControllerInterface? fileprivate var presentationData: PresentationData private var presentationDataDisposable: Disposable? @@ -10461,9 +10577,11 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc return self.displayNode as! PeerInfoScreenNode } - private let _ready = Promise() + private let _readyProxy = Promise() + private let _readyInternal = Promise() + private var readyInternalDisposable: Disposable? override public var ready: Promise { - return self._ready + return self._readyProxy } override public var customNavigationData: CustomViewControllerNavigationData? { @@ -10476,9 +10594,19 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc } } + override public var previousItem: NavigationPreviousAction? { + didSet { + if self.isNodeLoaded { + if let (layout, navigationHeight) = self.validLayout { + self.controllerNode.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate) + } + } + } + } + private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)? - public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool = false, hintGroupInCommon: PeerId? = nil, requestsContext: PeerInvitationImportersContext? = nil, forumTopicThread: ChatReplyThreadMessage? = nil) { + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool = false, hintGroupInCommon: PeerId? = nil, requestsContext: PeerInvitationImportersContext? = nil, forumTopicThread: ChatReplyThreadMessage? = nil, switchToRecommendedChannels: Bool = false) { self.context = context self.updatedPresentationData = updatedPresentationData self.peerId = peerId @@ -10490,6 +10618,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc self.isSettings = isSettings self.hintGroupInCommon = hintGroupInCommon self.requestsContext = requestsContext + self.switchToRecommendedChannels = switchToRecommendedChannels if let forumTopicThread = forumTopicThread { self.chatLocation = .replyThread(message: forumTopicThread) @@ -10502,9 +10631,9 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc let baseNavigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData) super.init(navigationBarPresentationData: NavigationBarPresentationData( theme: NavigationBarTheme( - buttonColor: avatarInitiallyExpanded ? .white : baseNavigationBarPresentationData.theme.buttonColor, - disabledButtonColor: baseNavigationBarPresentationData.theme.disabledButtonColor, - primaryTextColor: baseNavigationBarPresentationData.theme.primaryTextColor, + buttonColor: .white, + disabledButtonColor: .white, + primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, @@ -10512,6 +10641,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc badgeStrokeColor: baseNavigationBarPresentationData.theme.badgeStrokeColor, badgeTextColor: baseNavigationBarPresentationData.theme.badgeTextColor ), strings: baseNavigationBarPresentationData.strings)) + + self.navigationBar?.enableAutomaticBackButton = false if isSettings { let activeSessionsContextAndCountSignal = deferred { () -> Signal<(ActiveSessionsContext, Int, WebSessionsContext)?, NoError> in @@ -10710,6 +10841,9 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc if strongSelf.controllerNode.scrollNode.view.contentOffset.y > .ulpOfOne { return nil } + if strongSelf.controllerNode.initialExpandPanes { + return nil + } if isInteractive && strongSelf.controllerNode.headerNode.isAvatarExpanded { return nil } @@ -10723,8 +10857,6 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc } } - self.setStatusBarStyle(avatarInitiallyExpanded ? .White : self.presentationData.theme.rootController.statusBarStyle.style, animated: false) - self.scrollToTop = { [weak self] in self?.controllerNode.scrollToTop() } @@ -10793,6 +10925,19 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc return true } } + + self.readyInternalDisposable = (self._readyInternal.get() + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] _ in + guard let self else { + return + } + if self.controllerNode.initialExpandPanes { + self.controllerNode.expandTabs(animated: false) + } + self._readyProxy.set(.single(true)) + }) } required init(coder aDecoder: NSCoder) { @@ -10800,17 +10945,18 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc } deinit { + self.readyInternalDisposable?.dispose() self.presentationDataDisposable?.dispose() self.accountsAndPeersDisposable?.dispose() self.tabBarItemDisposable?.dispose() } override public func loadDisplayNode() { - self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages, isSettings: self.isSettings, hintGroupInCommon: self.hintGroupInCommon, requestsContext: self.requestsContext, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder) + self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages, isSettings: self.isSettings, hintGroupInCommon: self.hintGroupInCommon, requestsContext: self.requestsContext, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, initialPaneKey: self.switchToRecommendedChannels ? .recommended : nil) self.controllerNode.accountsAndPeers.set(self.accountsAndPeers.get() |> map { $0.1 }) self.controllerNode.activeSessionsContextAndCount.set(self.activeSessionsContextAndCount.get()) self.cachedDataPromise.set(self.controllerNode.cachedDataPromise.get()) - self._ready.set(self.controllerNode.ready.get()) + self._readyInternal.set(self.controllerNode.ready.get()) super.displayNodeDidLoad() } @@ -10835,7 +10981,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc self.controllerNode.resetHeaderExpansion() } } else { - self.controllerNode.updateNavigation(transition: .immediate, additive: false) + self.controllerNode.updateNavigation(transition: .immediate, additive: false, animateHeader: false) } } } @@ -10871,21 +11017,21 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc } } - func updateProfilePhoto(_ image: UIImage, mode: PeerInfoAvatarEditingMode) { + public func updateProfilePhoto(_ image: UIImage, mode: PeerInfoAvatarEditingMode) { if !self.isNodeLoaded { self.loadDisplayNode() } self.controllerNode.updateProfilePhoto(image, mode: mode) } - func updateProfileVideo(_ image: UIImage, mode: PeerInfoAvatarEditingMode, asset: Any?, adjustments: TGVideoEditAdjustments?, fallback: Bool = false) { + public func updateProfileVideo(_ image: UIImage, mode: PeerInfoAvatarEditingMode, asset: Any?, adjustments: TGVideoEditAdjustments?, fallback: Bool = false) { if !self.isNodeLoaded { self.loadDisplayNode() } self.controllerNode.updateProfileVideo(image, asset: asset, adjustments: adjustments, mode: mode) } - static func displayChatNavigationMenu(context: AccountContext, chatNavigationStack: [ChatNavigationStackItem], nextFolderId: Int32?, parentController: ViewController, backButtonView: UIView, navigationController: NavigationController, gesture: ContextGesture) { + public static func displayChatNavigationMenu(context: AccountContext, chatNavigationStack: [ChatNavigationStackItem], nextFolderId: Int32?, parentController: ViewController, backButtonView: UIView, navigationController: NavigationController, gesture: ContextGesture) { let peerMap = EngineDataMap( Set(chatNavigationStack.map(\.peerId)).map(TelegramEngine.EngineData.Item.Peer.Peer.init) ) @@ -10973,7 +11119,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc })) }))) } - let contextController = ContextController(presentationData: presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: parentController, sourceView: backButtonView, insets: UIEdgeInsets(), contentInsets: UIEdgeInsets(top: 0.0, left: -15.0, bottom: 0.0, right: -15.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .reference(PeerInfoControllerContextReferenceContentSource(controller: parentController, sourceView: backButtonView, insets: UIEdgeInsets(), contentInsets: UIEdgeInsets(top: 0.0, left: -15.0, bottom: 0.0, right: -15.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) parentController.presentInGlobalOverlay(contextController) }) } @@ -11220,9 +11366,9 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig } } - if self.screenNode.headerNode.isAvatarExpanded, let currentBackButtonArrow = topNavigationBar.makeTransitionBackArrowNode(accentColor: self.screenNode.headerNode.isAvatarExpanded ? .white : self.presentationData.theme.rootController.navigationBar.accentTextColor) { + if let currentBackButtonArrow = topNavigationBar.makeTransitionBackArrowNode(accentColor: .white) { self.currentBackButtonArrow = currentBackButtonArrow - self.addSubnode(currentBackButtonArrow) + //self.addSubnode(currentBackButtonArrow) } if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { @@ -11234,9 +11380,9 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig } } - if let currentBackButton = topNavigationBar.makeTransitionBackButtonNode(accentColor: self.screenNode.headerNode.isAvatarExpanded ? .white : self.presentationData.theme.rootController.navigationBar.accentTextColor) { + if let currentBackButton = topNavigationBar.makeTransitionBackButtonNode(accentColor: .white) { self.currentBackButton = currentBackButton - self.addSubnode(currentBackButton) + //self.addSubnode(currentBackButton) } if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { @@ -11308,10 +11454,12 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig if let backArrowView = headerView.backArrowView { let previousBackButtonArrowFrame = backArrowView.convert(backArrowView.bounds, to: bottomNavigationBar.view) previousBackButtonArrow.frame = previousBackButtonArrowFrame + transition.updateAlpha(layer: previousBackButtonArrow.layer, alpha: fraction) } } else { let previousBackButtonArrowFrame = bottomNavigationBar.backButtonArrow.view.convert(bottomNavigationBar.backButtonArrow.view.bounds, to: bottomNavigationBar.view) previousBackButtonArrow.frame = previousBackButtonArrowFrame + transition.updateAlpha(layer: previousBackButtonArrow.layer, alpha: fraction) } } @@ -11393,7 +11541,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?.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, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: transition, additive: 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?.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, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: transition, additive: false, animateHeader: true) } let titleScale = (fraction * previousTitleNode.view.bounds.height + (1.0 - fraction) * self.headerNode.titleNodeRawContainer.bounds.height) / previousTitleNode.view.bounds.height @@ -11567,7 +11715,7 @@ private final class PeerInfoContextReferenceContentSource: ContextReferenceConte } } -func presentAddMembersImpl(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, parentController: ViewController, groupPeer: Peer, selectAddMemberDisposable: MetaDisposable, addMemberDisposable: MetaDisposable) { +public func presentAddMembersImpl(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, parentController: ViewController, groupPeer: Peer, selectAddMemberDisposable: MetaDisposable, addMemberDisposable: MetaDisposable) { let members: Promise<[PeerId]> = Promise() if groupPeer.id.namespace == Namespaces.Peer.CloudChannel { /*var membersDisposable: Disposable? @@ -12244,3 +12392,21 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom }) } } + +private final class PeerInfoControllerContextReferenceContentSource: ContextReferenceContentSource { + let controller: ViewController + let sourceView: UIView + let insets: UIEdgeInsets + let contentInsets: UIEdgeInsets + + init(controller: ViewController, sourceView: UIView, insets: UIEdgeInsets, contentInsets: UIEdgeInsets = UIEdgeInsets()) { + self.controller = controller + self.sourceView = sourceView + self.insets = insets + self.contentInsets = contentInsets + } + + func transitionInfo() -> ContextControllerReferenceViewInfo? { + return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds.inset(by: self.insets), insets: self.contentInsets) + } +} diff --git a/submodules/TelegramUI/Sources/PeerInfoScreenMultilineInputtem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenMultilineInputtem.swift similarity index 100% rename from submodules/TelegramUI/Sources/PeerInfoScreenMultilineInputtem.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenMultilineInputtem.swift diff --git a/submodules/TelegramUI/Sources/PeerInfo/PhotoUpdateConfirmationController.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PhotoUpdateConfirmationController.swift similarity index 100% rename from submodules/TelegramUI/Sources/PeerInfo/PhotoUpdateConfirmationController.swift rename to submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PhotoUpdateConfirmationController.swift diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/BUILD b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/BUILD index b861093abe9..b7dd22ae251 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/BUILD +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/BUILD @@ -37,6 +37,7 @@ swift_library( "//submodules/MediaPickerUI", "//submodules/TelegramUI/Components/Stories/StoryContainerScreen", "//submodules/TelegramUI/Components/EmptyStateIndicatorComponent", + "//submodules/UIKitRuntimeUtils", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift index 556ecd5ab91..508c5fa3b88 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift @@ -27,6 +27,7 @@ import InvisibleInkDustNode import MediaPickerUI import StoryContainerScreen import EmptyStateIndicatorComponent +import UIKitRuntimeUtils private let mediaBadgeBackgroundColor = UIColor(white: 0.0, alpha: 0.6) private let mediaBadgeTextColor = UIColor.white @@ -483,213 +484,6 @@ private final class GenericItemLayer: CALayer, ItemLayer { } } -private final class CaptureProtectedItemLayer: AVSampleBufferDisplayLayer, ItemLayer { - var item: VisualMediaItem? - var viewCountLayer: DurationLayer? - var durationLayer: DurationLayer? - var leftShadowLayer: SimpleLayer? - var rightShadowLayer: SimpleLayer? - var minFactor: CGFloat = 1.0 - var selectionLayer: GridMessageSelectionLayer? - var dustLayer: MediaDustLayer? - var disposable: Disposable? - - var hasContents: Bool = false - - override init() { - super.init() - - self.contentsGravity = .resize - if #available(iOS 13.0, *) { - self.preventsCapture = true - self.preventsDisplaySleepDuringVideoPlayback = false - } - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - deinit { - self.disposable?.dispose() - } - - override func action(forKey event: String) -> CAAction? { - return nullAction - } - - private var layerContents: Any? - func getContents() -> Any? { - return self.layerContents - } - - func setContents(_ contents: Any?) { - self.layerContents = contents - - if let image = contents as? UIImage { - self.layerContents = image.cgImage - if let cmSampleBuffer = image.cmSampleBuffer { - self.enqueue(cmSampleBuffer) - } - } - } - - func setSpoilerContents(_ contents: Any?) { - if let image = contents as? UIImage { - self.dustLayer?.contents = image.cgImage - } - } - - func bind(item: VisualMediaItem) { - self.item = item - } - - func updateDuration(viewCount: Int32?, duration: Int32?, isMin: Bool, minFactor: CGFloat) { - self.minFactor = minFactor - - if let viewCount { - if let viewCountLayer = self.viewCountLayer { - viewCountLayer.update(viewCount: viewCount, isMin: isMin) - } else { - let viewCountLayer = DurationLayer() - viewCountLayer.contentsGravity = .topLeft - viewCountLayer.update(viewCount: viewCount, isMin: isMin) - self.addSublayer(viewCountLayer) - viewCountLayer.frame = CGRect(origin: CGPoint(x: 7.0, y: self.bounds.height - 4.0), size: CGSize()) - viewCountLayer.transform = CATransform3DMakeScale(minFactor, minFactor, 1.0) - self.viewCountLayer = viewCountLayer - } - } else if let viewCountLayer = self.viewCountLayer { - self.viewCountLayer = nil - viewCountLayer.removeFromSuperlayer() - } - - if let duration { - if let durationLayer = self.durationLayer { - durationLayer.update(duration: duration, isMin: isMin) - } else { - let durationLayer = DurationLayer() - durationLayer.update(duration: duration, isMin: isMin) - self.addSublayer(durationLayer) - durationLayer.frame = CGRect(origin: CGPoint(x: self.bounds.width - 3.0, y: self.bounds.height - 4.0), size: CGSize()) - durationLayer.transform = CATransform3DMakeScale(minFactor, minFactor, 1.0) - self.durationLayer = durationLayer - } - } else if let durationLayer = self.durationLayer { - self.durationLayer = nil - durationLayer.removeFromSuperlayer() - } - - let size = self.bounds.size - - if self.viewCountLayer != nil { - if self.leftShadowLayer == nil { - let leftShadowLayer = SimpleLayer() - self.leftShadowLayer = leftShadowLayer - self.insertSublayer(leftShadowLayer, at: 0) - leftShadowLayer.contents = leftShadowImage.cgImage - let shadowSize = CGSize(width: min(size.width, leftShadowImage.size.width), height: min(size.height, leftShadowImage.size.height)) - leftShadowLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: size.height - shadowSize.height), size: shadowSize) - } - } else { - if let leftShadowLayer = self.leftShadowLayer { - self.leftShadowLayer = nil - leftShadowLayer.removeFromSuperlayer() - } - } - - if self.durationLayer != nil { - if self.rightShadowLayer == nil { - let rightShadowLayer = SimpleLayer() - self.rightShadowLayer = rightShadowLayer - self.insertSublayer(rightShadowLayer, at: 0) - rightShadowLayer.contents = rightShadowImage.cgImage - let shadowSize = CGSize(width: min(size.width, rightShadowImage.size.width), height: min(size.height, rightShadowImage.size.height)) - rightShadowLayer.frame = CGRect(origin: CGPoint(x: size.width - shadowSize.width, y: size.height - shadowSize.height), size: shadowSize) - } - } else { - if let rightShadowLayer = self.rightShadowLayer { - self.rightShadowLayer = nil - rightShadowLayer.removeFromSuperlayer() - } - } - } - - func updateSelection(theme: CheckNodeTheme, isSelected: Bool?, animated: Bool) { - if let isSelected = isSelected { - if let selectionLayer = self.selectionLayer { - selectionLayer.updateSelected(isSelected, animated: animated) - } else { - let selectionLayer = GridMessageSelectionLayer(theme: theme) - selectionLayer.updateSelected(isSelected, animated: false) - self.selectionLayer = selectionLayer - self.addSublayer(selectionLayer) - if !self.bounds.isEmpty { - selectionLayer.frame = CGRect(origin: CGPoint(), size: self.bounds.size) - selectionLayer.updateLayout(size: self.bounds.size) - if animated { - selectionLayer.animateIn() - } - } - } - } else if let selectionLayer = self.selectionLayer { - self.selectionLayer = nil - if animated { - selectionLayer.animateOut { [weak selectionLayer] in - selectionLayer?.removeFromSuperlayer() - } - } else { - selectionLayer.removeFromSuperlayer() - } - } - } - - func updateHasSpoiler(hasSpoiler: Bool) { - if hasSpoiler { - if let _ = self.dustLayer { - } else { - let dustLayer = MediaDustLayer() - self.dustLayer = dustLayer - self.addSublayer(dustLayer) - if !self.bounds.isEmpty { - dustLayer.frame = CGRect(origin: CGPoint(), size: self.bounds.size) - dustLayer.updateLayout(size: self.bounds.size) - } - } - } else if let dustLayer = self.dustLayer { - self.dustLayer = nil - dustLayer.removeFromSuperlayer() - } - } - - func unbind() { - self.item = nil - } - - func needsShimmer() -> Bool { - return !self.hasContents - } - - func update(size: CGSize, insets: UIEdgeInsets, displayItem: SparseItemGridDisplayItem, binding: SparseItemGridBinding, item: SparseItemGrid.Item?) { - if let viewCountLayer = self.viewCountLayer { - viewCountLayer.frame = CGRect(origin: CGPoint(x: 7.0, y: size.height - 4.0), size: CGSize()) - } - if let durationLayer = self.durationLayer { - durationLayer.frame = CGRect(origin: CGPoint(x: size.width - 3.0, y: size.height - 4.0), size: CGSize()) - } - - if let leftShadowLayer = self.leftShadowLayer { - let shadowSize = CGSize(width: min(size.width, leftShadowImage.size.width), height: min(size.height, leftShadowImage.size.height)) - leftShadowLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: size.height - shadowSize.height), size: shadowSize) - } - - if let rightShadowLayer = self.rightShadowLayer { - let shadowSize = CGSize(width: min(size.width, rightShadowImage.size.width), height: min(size.height, rightShadowImage.size.height)) - rightShadowLayer.frame = CGRect(origin: CGPoint(x: size.width - shadowSize.width, y: size.height - shadowSize.height), size: shadowSize) - } - } -} - private final class ItemTransitionView: UIView { private weak var itemLayer: CALayer? private var copyDurationLayer: SimpleLayer? @@ -712,11 +506,7 @@ private final class ItemTransitionView: UIView { var durationLayer: CALayer? var leftShadowLayer: CALayer? var rightShadowLayer: CALayer? - if let itemLayer = itemLayer as? CaptureProtectedItemLayer { - viewCountLayer = itemLayer.viewCountLayer - durationLayer = itemLayer.durationLayer - self.layer.contents = itemLayer.getContents() - } else if let itemLayer = itemLayer as? GenericItemLayer { + if let itemLayer = itemLayer as? GenericItemLayer { viewCountLayer = itemLayer.viewCountLayer durationLayer = itemLayer.durationLayer leftShadowLayer = itemLayer.leftShadowLayer @@ -852,7 +642,9 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding { func createLayer(item: SparseItemGrid.Item) -> SparseItemGridLayer? { if let item = item as? VisualMediaItem, item.story.isForwardingDisabled { - return CaptureProtectedItemLayer() + let layer = GenericItemLayer() + setLayerDisableScreenshots(layer, true) + return layer } else { return GenericItemLayer() } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift index a507d30de71..46594cbc522 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift @@ -27,6 +27,7 @@ import ChatControllerInteraction import InvisibleInkDustNode import MediaPickerUI import ChatControllerInteraction +import UIKitRuntimeUtils public enum PeerInfoPaneKey: Int32 { case members @@ -38,6 +39,7 @@ public enum PeerInfoPaneKey: Int32 { case links case gifs case groupsInCommon + case recommended } public struct PeerInfoStatusData: Equatable { @@ -516,170 +518,6 @@ private final class GenericItemLayer: CALayer, ItemLayer { } } -private final class CaptureProtectedItemLayer: AVSampleBufferDisplayLayer, ItemLayer { - var item: VisualMediaItem? - var durationLayer: DurationLayer? - var rightShadowLayer: SimpleLayer? - var minFactor: CGFloat = 1.0 - var selectionLayer: GridMessageSelectionLayer? - var dustLayer: MediaDustLayer? - var disposable: Disposable? - - var hasContents: Bool = false - - override init() { - super.init() - - self.contentsGravity = .resize - if #available(iOS 13.0, *) { - self.preventsCapture = true - self.preventsDisplaySleepDuringVideoPlayback = false - } - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - deinit { - self.disposable?.dispose() - } - - override func action(forKey event: String) -> CAAction? { - return nullAction - } - - private var layerContents: Any? - func getContents() -> Any? { - return self.layerContents - } - - func setContents(_ contents: Any?) { - self.layerContents = contents - - if let image = contents as? UIImage { - self.layerContents = image.cgImage - if let cmSampleBuffer = image.cmSampleBuffer { - self.enqueue(cmSampleBuffer) - } - } - } - - func setSpoilerContents(_ contents: Any?) { - if let image = contents as? UIImage { - self.dustLayer?.contents = image.cgImage - } - } - - func bind(item: VisualMediaItem) { - self.item = item - } - - func updateDuration(duration: Int32?, isMin: Bool, minFactor: CGFloat) { - self.minFactor = minFactor - - if let duration { - if let durationLayer = self.durationLayer { - durationLayer.update(duration: duration, isMin: isMin) - } else { - let durationLayer = DurationLayer() - durationLayer.update(duration: duration, isMin: isMin) - self.addSublayer(durationLayer) - durationLayer.frame = CGRect(origin: CGPoint(x: self.bounds.width - 3.0, y: self.bounds.height - 3.0), size: CGSize()) - durationLayer.transform = CATransform3DMakeScale(minFactor, minFactor, 1.0) - self.durationLayer = durationLayer - } - } else if let durationLayer = self.durationLayer { - self.durationLayer = nil - durationLayer.removeFromSuperlayer() - } - - let size = self.bounds.size - - if self.durationLayer != nil { - if self.rightShadowLayer == nil { - let rightShadowLayer = SimpleLayer() - self.rightShadowLayer = rightShadowLayer - self.insertSublayer(rightShadowLayer, at: 0) - rightShadowLayer.contents = rightShadowImage.cgImage - let shadowSize = CGSize(width: min(size.width, rightShadowImage.size.width), height: min(size.height, rightShadowImage.size.height)) - rightShadowLayer.frame = CGRect(origin: CGPoint(x: size.width - shadowSize.width, y: size.height - shadowSize.height), size: shadowSize) - } - } else { - if let rightShadowLayer = self.rightShadowLayer { - self.rightShadowLayer = nil - rightShadowLayer.removeFromSuperlayer() - } - } - } - - func updateSelection(theme: CheckNodeTheme, isSelected: Bool?, animated: Bool) { - if let isSelected = isSelected { - if let selectionLayer = self.selectionLayer { - selectionLayer.updateSelected(isSelected, animated: animated) - } else { - let selectionLayer = GridMessageSelectionLayer(theme: theme) - selectionLayer.updateSelected(isSelected, animated: false) - self.selectionLayer = selectionLayer - self.addSublayer(selectionLayer) - if !self.bounds.isEmpty { - selectionLayer.frame = CGRect(origin: CGPoint(), size: self.bounds.size) - selectionLayer.updateLayout(size: self.bounds.size) - if animated { - selectionLayer.animateIn() - } - } - } - } else if let selectionLayer = self.selectionLayer { - self.selectionLayer = nil - if animated { - selectionLayer.animateOut { [weak selectionLayer] in - selectionLayer?.removeFromSuperlayer() - } - } else { - selectionLayer.removeFromSuperlayer() - } - } - } - - func updateHasSpoiler(hasSpoiler: Bool) { - if hasSpoiler { - if let _ = self.dustLayer { - } else { - let dustLayer = MediaDustLayer() - self.dustLayer = dustLayer - self.addSublayer(dustLayer) - if !self.bounds.isEmpty { - dustLayer.frame = CGRect(origin: CGPoint(), size: self.bounds.size) - dustLayer.updateLayout(size: self.bounds.size) - } - } - } else if let dustLayer = self.dustLayer { - self.dustLayer = nil - dustLayer.removeFromSuperlayer() - } - } - - func unbind() { - self.item = nil - } - - func needsShimmer() -> Bool { - return !self.hasContents - } - - func update(size: CGSize, insets: UIEdgeInsets, displayItem: SparseItemGridDisplayItem, binding: SparseItemGridBinding, item: SparseItemGrid.Item?) { - if let durationLayer = self.durationLayer { - durationLayer.frame = CGRect(origin: CGPoint(x: size.width - 3.0, y: size.height - 3.0), size: CGSize()) - } - - if let rightShadowLayer = self.rightShadowLayer { - let shadowSize = CGSize(width: min(size.width, rightShadowImage.size.width), height: min(size.height, rightShadowImage.size.height)) - rightShadowLayer.frame = CGRect(origin: CGPoint(x: size.width - shadowSize.width, y: size.height - shadowSize.height), size: shadowSize) - } - } -} - private final class ItemView: UIView, SparseItemGridView { var item: VisualMediaItem? var disposable: Disposable? @@ -1004,7 +842,9 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme return nil } if self.captureProtected { - return CaptureProtectedItemLayer() + let layer = GenericItemLayer() + setLayerDisableScreenshots(layer, true) + return layer } else { return GenericItemLayer() } diff --git a/submodules/TelegramUI/Components/PlainButtonComponent/Sources/PlainButtonComponent.swift b/submodules/TelegramUI/Components/PlainButtonComponent/Sources/PlainButtonComponent.swift index ddceea53a98..a6e568adb88 100644 --- a/submodules/TelegramUI/Components/PlainButtonComponent/Sources/PlainButtonComponent.swift +++ b/submodules/TelegramUI/Components/PlainButtonComponent/Sources/PlainButtonComponent.swift @@ -14,17 +14,20 @@ public final class PlainButtonComponent: Component { public let effectAlignment: EffectAlignment public let minSize: CGSize? public let action: () -> Void + public let isEnabled: Bool public init( content: AnyComponent, effectAlignment: EffectAlignment, minSize: CGSize? = nil, - action: @escaping () -> Void + action: @escaping () -> Void, + isEnabled: Bool = true ) { self.content = content self.effectAlignment = effectAlignment self.minSize = minSize self.action = action + self.isEnabled = isEnabled } public static func ==(lhs: PlainButtonComponent, rhs: PlainButtonComponent) -> Bool { @@ -37,6 +40,9 @@ public final class PlainButtonComponent: Component { if lhs.minSize != rhs.minSize { return false } + if lhs.isEnabled != rhs.isEnabled { + return false + } return true } @@ -72,7 +78,7 @@ public final class PlainButtonComponent: Component { transition.setScale(layer: self.contentContainer.layer, scale: topScale) } else { self.contentContainer.alpha = 1.0 - self.contentContainer.layer.animateAlpha(from: 7, to: 1.0, duration: 0.2) + self.contentContainer.layer.animateAlpha(from: 0.7, to: 1.0, duration: 0.2) let transition = Transition(animation: .none) transition.setScale(layer: self.contentContainer.layer, scale: 1.0) @@ -121,7 +127,7 @@ public final class PlainButtonComponent: Component { self.component = component self.componentState = state - self.isEnabled = true + self.isEnabled = component.isEnabled let contentAlpha: CGFloat = 1.0 diff --git a/submodules/TelegramUI/Components/Resources/FetchVideoMediaResource/Sources/FetchVideoMediaResource.swift b/submodules/TelegramUI/Components/Resources/FetchVideoMediaResource/Sources/FetchVideoMediaResource.swift index c766b9e28b6..e9e4eb013bf 100644 --- a/submodules/TelegramUI/Components/Resources/FetchVideoMediaResource/Sources/FetchVideoMediaResource.swift +++ b/submodules/TelegramUI/Components/Resources/FetchVideoMediaResource/Sources/FetchVideoMediaResource.swift @@ -857,11 +857,16 @@ private extension MediaEditorValues { videoIsMuted: false, videoIsFullHd: true, videoIsMirrored: false, + videoVolume: 1.0, additionalVideoPath: nil, + additionalVideoIsDual: false, additionalVideoPosition: nil, additionalVideoScale: nil, additionalVideoRotation: nil, additionalVideoPositionChanges: [], + additionalVideoTrimRange: nil, + additionalVideoOffset: nil, + additionalVideoVolume: nil, drawing: nil, entities: [], toolValues: [:], @@ -993,11 +998,16 @@ private extension MediaEditorValues { videoIsMuted: legacyAdjustments.sendAsGif, videoIsFullHd: true, videoIsMirrored: false, + videoVolume: 1.0, additionalVideoPath: nil, + additionalVideoIsDual: false, additionalVideoPosition: nil, additionalVideoScale: nil, additionalVideoRotation: nil, additionalVideoPositionChanges: [], + additionalVideoTrimRange: nil, + additionalVideoOffset: nil, + additionalVideoVolume: nil, drawing: drawing, entities: entities, toolValues: toolValues, diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/BUILD b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/BUILD index 2dc1c20ba16..dd8d1627dba 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/BUILD +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/BUILD @@ -26,6 +26,8 @@ swift_library( "//submodules/SolidRoundedButtonNode", "//submodules/AppBundle", "//submodules/PremiumUI", + "//submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent", + "//submodules/AvatarNode", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/EmojiPickerItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/EmojiPickerItem.swift index 7c8f6f686bc..c858d11c1a8 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/EmojiPickerItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/EmojiPickerItem.swift @@ -16,6 +16,8 @@ final class EmojiPickerItem: ListViewItem, ItemListItem { let strings: PresentationStrings let emojiContent: EmojiPagerContentComponent let backgroundIconColor: UIColor + let isProfileColor: Bool + let hasRemoveButton: Bool let sectionId: ItemListSectionId init( @@ -24,6 +26,8 @@ final class EmojiPickerItem: ListViewItem, ItemListItem { strings: PresentationStrings, emojiContent: EmojiPagerContentComponent, backgroundIconColor: UIColor, + isProfileColor: Bool, + hasRemoveButton: Bool, sectionId: ItemListSectionId ) { self.context = context @@ -31,6 +35,8 @@ final class EmojiPickerItem: ListViewItem, ItemListItem { self.strings = strings self.emojiContent = emojiContent self.backgroundIconColor = backgroundIconColor + self.isProfileColor = isProfileColor + self.hasRemoveButton = hasRemoveButton self.sectionId = sectionId } @@ -103,13 +109,20 @@ final class EmojiPickerItemNode: ListViewItemNode { } func asyncLayout() -> (_ item: EmojiPickerItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { - let currentItem = self.item - return { item, params, neighbors in let insets: UIEdgeInsets let separatorHeight = UIScreenPixel - var contentSize = CGSize(width: params.width, height: params.availableHeight - 452.0) + var contentHeight: CGFloat + if item.isProfileColor { + contentHeight = params.availableHeight - 352.0 - 10.0 + if item.hasRemoveButton { + contentHeight -= 44.0 + } + } else { + contentHeight = params.availableHeight - 452.0 + } + var contentSize = CGSize(width: params.width, height: contentHeight) if params.width <= 320.0 { contentSize.height += 77.0 } @@ -121,16 +134,6 @@ final class EmojiPickerItemNode: ListViewItemNode { return (layout, { [weak self] in if let strongSelf = self { strongSelf.item = item - - if let currentItem, currentItem.backgroundIconColor != item.backgroundIconColor { - if let snapshot = strongSelf.view.snapshotView(afterScreenUpdates: false) { - snapshot.frame = CGRect(origin: CGPoint(x: 0.0, y: -insets.top), size: snapshot.frame.size) - strongSelf.view.addSubview(snapshot) - snapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 0.25, removeOnCompletion: false, completion: { _ in - snapshot.removeFromSuperview() - }) - } - } strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift index 54e1c3e99a3..920bbc1b469 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift @@ -168,7 +168,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { return { item, params, neighbors in if currentBackgroundNode == nil { currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false) - currentBackgroundNode?.update(wallpaper: item.wallpaper) + currentBackgroundNode?.update(wallpaper: item.wallpaper, animated: false) currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners) } @@ -184,7 +184,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { var peers = SimpleDictionary() var messages = SimpleDictionary() - peers[authorPeerId] = TelegramUser(id: authorPeerId, accessHash: nil, firstName: messageItem.author, lastName: "", username: nil, phone: nil, photo: messageItem.photo, botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: messageItem.nameColor, backgroundEmojiId: messageItem.backgroundEmojiId) + peers[authorPeerId] = TelegramUser(id: authorPeerId, accessHash: nil, firstName: messageItem.author, lastName: "", username: nil, phone: nil, photo: messageItem.photo, botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: messageItem.nameColor, backgroundEmojiId: messageItem.backgroundEmojiId, profileColor: nil, profileBackgroundEmojiId: nil) let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) if let (_, text) = messageItem.reply { @@ -197,7 +197,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { } let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[authorPeerId], text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)] : [], media: media, peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, isCentered: false)) + items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false)) } var nodes: [ListViewItemNode] = [] @@ -254,7 +254,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { strongSelf.item = item if let currentBackgroundNode { - currentBackgroundNode.update(wallpaper: item.wallpaper) + currentBackgroundNode.update(wallpaper: item.wallpaper, animated: false) currentBackgroundNode.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) } @@ -296,7 +296,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { headerNode.updateLayoutInternal(size: headerFrame.size, leftInset: leftInset, rightInset: rightInset) headerNode.updateStickDistanceFactor(stickLocationDistanceFactor, transition: .immediate) } else { - headerNode = header.node(synchronousLoad: false) + headerNode = header.node(synchronousLoad: true) if headerNode.item !== header { header.updateNode(headerNode, previous: nil, next: nil) headerNode.item = header diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorItem.swift index c628bc18953..25c36e87b95 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorItem.swift @@ -127,7 +127,7 @@ private func generateRingImage(nameColor: PeerNameColors.Colors) -> UIImage? { }) } -func generatePeerNameColorImage(nameColor: PeerNameColors.Colors, isDark: Bool, bounds: CGSize = CGSize(width: 40.0, height: 40.0), size: CGSize = CGSize(width: 40.0, height: 40.0)) -> UIImage? { +public func generatePeerNameColorImage(nameColor: PeerNameColors.Colors, isDark: Bool, bounds: CGSize = CGSize(width: 40.0, height: 40.0), size: CGSize = CGSize(width: 40.0, height: 40.0)) -> UIImage? { return generateImage(bounds, rotatedContext: { contextSize, context in let bounds = CGRect(origin: CGPoint(), size: contextSize) context.clear(bounds) @@ -179,6 +179,37 @@ func generatePeerNameColorImage(nameColor: PeerNameColors.Colors, isDark: Bool, }) } +public func generateSettingsMenuPeerColorsLabelIcon(colors: [PeerNameColors.Colors]) -> UIImage { + let iconWidth: CGFloat = 24.0 + let iconSpacing: CGFloat = 18.0 + let borderWidth: CGFloat = 2.0 + + if colors.isEmpty { + return generateSingleColorImage(size: CGSize(width: iconWidth, height: iconWidth), color: .clear)! + } + + return generateImage(CGSize(width: CGFloat(max(0, colors.count - 1)) * iconSpacing + CGFloat(colors.count == 0 ? 0 : 1) * iconWidth, height: 24.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + for i in 0 ..< colors.count { + let iconFrame = CGRect(origin: CGPoint(x: CGFloat(i) * iconSpacing, y: 0.0), size: CGSize(width: iconWidth, height: iconWidth)) + context.setBlendMode(.copy) + context.setFillColor(UIColor.clear.cgColor) + context.fillEllipse(in: iconFrame.insetBy(dx: -borderWidth, dy: -borderWidth)) + context.setBlendMode(.normal) + + if let image = generatePeerNameColorImage(nameColor: colors[i], isDark: false, bounds: iconFrame.size, size: iconFrame.size)?.cgImage { + context.saveGState() + context.translateBy(x: iconFrame.midX, y: iconFrame.midY) + context.scaleBy(x: 1.0, y: -1.0) + context.translateBy(x: -iconFrame.midX, y: -iconFrame.midY) + context.draw(image, in: iconFrame) + context.restoreGState() + } + } + })! +} + private final class PeerNameColorIconItemNode : ListViewItemNode { private let containerNode: ContextControllerSourceNode private let fillNode: ASImageNode @@ -287,13 +318,15 @@ final class PeerNameColorItem: ListViewItem, ItemListItem { let theme: PresentationTheme let colors: PeerNameColors - let currentColor: PeerNameColor + let isProfile: Bool + let currentColor: PeerNameColor? let updated: (PeerNameColor) -> Void let tag: ItemListItemTag? - init(theme: PresentationTheme, colors: PeerNameColors, currentColor: PeerNameColor, updated: @escaping (PeerNameColor) -> Void, tag: ItemListItemTag? = nil, sectionId: ItemListSectionId) { + init(theme: PresentationTheme, colors: PeerNameColors, isProfile: Bool, currentColor: PeerNameColor?, updated: @escaping (PeerNameColor) -> Void, tag: ItemListItemTag? = nil, sectionId: ItemListSectionId) { self.theme = theme self.colors = colors + self.isProfile = isProfile self.currentColor = currentColor self.updated = updated self.tag = tag @@ -436,7 +469,13 @@ final class PeerNameColorItemNode: ListViewItemNode, ItemListItemNode { let options = ListViewDeleteAndInsertOptions() var scrollToItem: ListViewScrollToItem? if !self.initialized || transition.updatePosition || !self.tapping { - if let index = item.colors.displayOrder.firstIndex(where: { $0 == item.currentColor.rawValue }) { + let displayOrder: [Int32] + if item.isProfile { + displayOrder = item.colors.profileDisplayOrder + } else { + displayOrder = item.colors.displayOrder + } + if let index = displayOrder.firstIndex(where: { $0 == item.currentColor?.rawValue }) { scrollToItem = ListViewScrollToItem(index: index, position: .bottom(-70.0), animated: false, curve: .Default(duration: 0.0), directionHint: .Down) self.initialized = true } @@ -492,12 +531,17 @@ final class PeerNameColorItemNode: ListViewItemNode, ItemListItemNode { let hasCorners = itemListHasRoundedBlockLayout(params) var hasTopCorners = false var hasBottomCorners = false - switch neighbors.top { + if item.currentColor != nil { + switch neighbors.top { case .sameSection(false): strongSelf.topStripeNode.isHidden = true default: hasTopCorners = true strongSelf.topStripeNode.isHidden = hasCorners + } + } else { + strongSelf.topStripeNode.isHidden = true + hasTopCorners = true } let bottomStripeInset: CGFloat let bottomStripeOffset: CGFloat @@ -531,10 +575,21 @@ final class PeerNameColorItemNode: ListViewItemNode, ItemListItemNode { var entries: [PeerNameColorEntry] = [] + let displayOrder: [Int32] + if item.isProfile { + displayOrder = item.colors.profileDisplayOrder + } else { + displayOrder = item.colors.displayOrder + } var i: Int = 0 - for index in item.colors.displayOrder { + for index in displayOrder { let color = PeerNameColor(rawValue: index) - let colors = item.colors.get(color, dark: item.theme.overallDarkAppearance) + let colors: PeerNameColors.Colors + if item.isProfile { + colors = item.colors.getProfile(color, dark: item.theme.overallDarkAppearance, subject: .palette) + } else { + colors = item.colors.get(color, dark: item.theme.overallDarkAppearance) + } entries.append(.color(i, color, colors, item.theme.overallDarkAppearance, color == item.currentColor)) i += 1 diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift new file mode 100644 index 00000000000..9db8346dd77 --- /dev/null +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift @@ -0,0 +1,281 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import SwiftSignalKit +import TelegramCore +import Postbox +import TelegramPresentationData +import TelegramUIPreferences +import ItemListUI +import PresentationDataUtils +import AccountContext +import ComponentFlow +import PeerInfoCoverComponent +import AvatarNode +import EmojiStatusComponent + +final class PeerNameColorProfilePreviewItem: ListViewItem, ItemListItem { + let context: AccountContext + let theme: PresentationTheme + let componentTheme: PresentationTheme + let strings: PresentationStrings + let sectionId: ItemListSectionId + let peer: EnginePeer? + let files: [Int64: TelegramMediaFile] + let nameDisplayOrder: PresentationPersonNameOrder + + init(context: AccountContext, theme: PresentationTheme, componentTheme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, peer: EnginePeer?, files: [Int64: TelegramMediaFile], nameDisplayOrder: PresentationPersonNameOrder) { + self.context = context + self.theme = theme + self.componentTheme = componentTheme + self.strings = strings + self.sectionId = sectionId + self.peer = peer + self.files = files + self.nameDisplayOrder = nameDisplayOrder + } + + func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { + async { + let node = PeerNameColorProfilePreviewItemNode() + let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) + + node.contentSize = layout.contentSize + node.insets = layout.insets + + Queue.mainQueue().async { + completion(node, { + return (nil, { _ in apply() }) + }) + } + } + } + + 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? PeerNameColorProfilePreviewItemNode { + let makeLayout = nodeValue.asyncLayout() + + async { + let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) + Queue.mainQueue().async { + completion(layout, { _ in + apply() + }) + } + } + } + } + } +} + +final class PeerNameColorProfilePreviewItemNode: ListViewItemNode { + private let background = ComponentView() + private let avatarNode: AvatarNode + private let title = ComponentView() + private let subtitle = ComponentView() + private var icon: ComponentView? + + private let topStripeNode: ASDisplayNode + private let bottomStripeNode: ASDisplayNode + private let maskNode: ASImageNode + + private var item: PeerNameColorProfilePreviewItem? + + init() { + self.topStripeNode = ASDisplayNode() + self.topStripeNode.isLayerBacked = true + + self.bottomStripeNode = ASDisplayNode() + self.bottomStripeNode.isLayerBacked = true + + self.maskNode = ASImageNode() + + let avatarFont = avatarPlaceholderFont(size: floor(100.0 * 16.0 / 37.0)) + self.avatarNode = AvatarNode(font: avatarFont) + + super.init(layerBacked: false, dynamicBounce: false) + + self.clipsToBounds = true + self.isUserInteractionEnabled = false + } + + deinit { + } + + func asyncLayout() -> (_ item: PeerNameColorProfilePreviewItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { + return { [weak self] item, params, neighbors in + let separatorHeight = UIScreenPixel + + let contentSize = CGSize(width: params.width, height: 210.0) + var insets = itemListNeighborsGroupedInsets(neighbors, params) + if params.width <= 320.0 { + insets.top = 0.0 + } + + let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) + let layoutSize = layout.size + + return (layout, { [weak self] in + guard let self else { + return + } + if let previousItem = self.item, (previousItem.peer?.profileColor != item.peer?.profileColor) || (previousItem.peer?.profileBackgroundEmojiId != item.peer?.profileBackgroundEmojiId) { + UIView.transition(with: self.view, duration: 0.2, options: UIView.AnimationOptions.transitionCrossDissolve, animations: { + }) + } + self.item = item + + self.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor + self.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor + + if self.topStripeNode.supernode == nil { + self.addSubnode(self.topStripeNode) + } + if self.bottomStripeNode.supernode == nil { + self.addSubnode(self.bottomStripeNode) + } + if self.maskNode.supernode == nil { + self.addSubnode(self.maskNode) + } + + let hasCorners = itemListHasRoundedBlockLayout(params) + var hasTopCorners = false + var hasBottomCorners = false + switch neighbors.top { + case .sameSection(false): + self.topStripeNode.isHidden = true + default: + hasTopCorners = true + self.topStripeNode.isHidden = hasCorners + } + let bottomStripeInset: CGFloat + let bottomStripeOffset: CGFloat + switch neighbors.bottom { + case .sameSection(false): + bottomStripeInset = 0.0 + bottomStripeOffset = -separatorHeight + self.bottomStripeNode.isHidden = item.peer?.profileColor == nil + default: + bottomStripeInset = 0.0 + bottomStripeOffset = 0.0 + hasBottomCorners = true + self.bottomStripeNode.isHidden = hasCorners + } + + self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil + + let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) + + let coverFrame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0) + + let avatarSize: CGFloat = 104.0 + let avatarFrame = CGRect(origin: CGPoint(x: floor((coverFrame.width - avatarSize) * 0.5), y: coverFrame.minY + 24.0), size: CGSize(width: avatarSize, height: avatarSize)) + + let _ = self.background.update( + transition: .immediate, + component: AnyComponent(PeerInfoCoverComponent( + context: item.context, + peer: item.peer, + files: item.files, + isDark: item.theme.overallDarkAppearance, + avatarCenter: avatarFrame.center, + avatarScale: 1.0, + defaultHeight: coverFrame.height, + avatarTransitionFraction: 0.0, + patternTransitionFraction: 0.0 + )), + environment: {}, + containerSize: coverFrame.size + ) + if let backgroundView = self.background.view { + if backgroundView.superview == nil { + backgroundView.clipsToBounds = true + self.view.insertSubview(backgroundView, at: 0) + } + backgroundView.frame = coverFrame + } + + let clipStyle: AvatarNodeClipStyle + switch item.peer { + case let .channel(channel) where channel.isForum: + clipStyle = .roundedRect + default: + clipStyle = .round + } + self.avatarNode.setPeer( + context: item.context, + theme: item.theme, + peer: item.peer, + clipStyle: clipStyle, + synchronousLoad: true, + displayDimensions: avatarFrame.size + ) + if self.avatarNode.supernode == nil { + self.addSubnode(self.avatarNode) + } + self.avatarNode.frame = avatarFrame.offsetBy(dx: coverFrame.minX, dy: coverFrame.minY) + + let backgroundColor: UIColor + let titleColor: UIColor + let subtitleColor: UIColor + if let peer = item.peer, let profileColor = peer.profileColor { + titleColor = .white + backgroundColor = item.context.peerNameColors.getProfile(profileColor).main + subtitleColor = UIColor(white: 1.0, alpha: 0.6).blitOver(backgroundColor.withMultiplied(hue: 1.0, saturation: 2.2, brightness: 1.5), alpha: 1.0) + } else { + titleColor = item.theme.list.itemPrimaryTextColor + subtitleColor = item.theme.list.itemSecondaryTextColor + backgroundColor = .clear + } + + let titleString: String = item.peer?.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) ?? " " + let titleSize = self.title.update( + transition: .immediate, + component: AnyComponent(Text( + text: titleString, font: Font.semibold(28.0), color: titleColor + )), + environment: {}, + containerSize: CGSize(width: coverFrame.width - 16.0, height: 100.0) + ) + let titleFrame = CGRect(origin: CGPoint(x: coverFrame.minX + floor((coverFrame.width - titleSize.width) * 0.5), y: avatarFrame.maxY + 10.0), size: titleSize) + if let titleView = self.title.view { + if titleView.superview == nil { + self.view.addSubview(titleView) + } + titleView.frame = titleFrame + } + + let subtitleString: String = item.strings.LastSeen_JustNow + let subtitleSize = self.subtitle.update( + transition: .immediate, + component: AnyComponent(Text( + text: subtitleString, font: Font.regular(18.0), color: subtitleColor + )), + environment: {}, + containerSize: CGSize(width: coverFrame.width - 16.0, height: 100.0) + ) + let subtitleFrame = CGRect(origin: CGPoint(x: coverFrame.minX + floor((coverFrame.width - subtitleSize.width) * 0.5), y: titleFrame.maxY + 3.0), size: subtitleSize) + if let subtitleView = self.subtitle.view { + if subtitleView.superview == nil { + self.view.addSubview(subtitleView) + } + subtitleView.frame = subtitleFrame + } + + self.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0) + self.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) + self.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) + }) + } + } + + override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) + } + + override func animateRemoved(_ currentTimestamp: Double, duration: Double) { + self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) + } +} diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift index 70e291a38e0..10108d5f3c5 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift @@ -16,17 +16,20 @@ import PremiumUI private final class PeerNameColorScreenArguments { let context: AccountContext - let updateNameColor: (PeerNameColor) -> Void - let updateBackgroundEmojiId: (Int64?) -> Void + let updateNameColor: (PeerNameColor?) -> Void + let updateBackgroundEmojiId: (Int64?, TelegramMediaFile?) -> Void + let resetColor: () -> Void init( context: AccountContext, - updateNameColor: @escaping (PeerNameColor) -> Void, - updateBackgroundEmojiId: @escaping (Int64?) -> Void + updateNameColor: @escaping (PeerNameColor?) -> Void, + updateBackgroundEmojiId: @escaping (Int64?, TelegramMediaFile?) -> Void, + resetColor: @escaping () -> Void ) { self.context = context self.updateNameColor = updateNameColor self.updateBackgroundEmojiId = updateBackgroundEmojiId + self.resetColor = resetColor } } @@ -39,7 +42,9 @@ private enum PeerNameColorScreenEntry: ItemListNodeEntry { enum StableId: Hashable { case colorHeader case colorMessage + case colorProfile case colorPicker + case removeColor case colorDescription case backgroundEmojiHeader case backgroundEmoji @@ -47,14 +52,16 @@ private enum PeerNameColorScreenEntry: ItemListNodeEntry { case colorHeader(String) case colorMessage(wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, items: [PeerNameColorChatPreviewItem.MessageItem]) - case colorPicker(colors: PeerNameColors, currentColor: PeerNameColor) + case colorProfile(peer: EnginePeer?, files: [Int64: TelegramMediaFile], nameDisplayOrder: PresentationPersonNameOrder) + case colorPicker(colors: PeerNameColors, currentColor: PeerNameColor?, isProfile: Bool) + case removeColor case colorDescription(String) case backgroundEmojiHeader(String, String?) - case backgroundEmoji(EmojiPagerContentComponent, UIColor) + case backgroundEmoji(EmojiPagerContentComponent, UIColor, Bool, Bool) var section: ItemListSectionId { switch self { - case .colorHeader, .colorMessage, .colorPicker, .colorDescription: + case .colorHeader, .colorMessage, .colorProfile, .colorPicker, .removeColor, .colorDescription: return PeerNameColorScreenSection.nameColor.rawValue case .backgroundEmojiHeader, .backgroundEmoji: return PeerNameColorScreenSection.backgroundEmoji.rawValue @@ -67,8 +74,12 @@ private enum PeerNameColorScreenEntry: ItemListNodeEntry { return .colorHeader case .colorMessage: return .colorMessage + case .colorProfile: + return .colorProfile case .colorPicker: return .colorPicker + case .removeColor: + return.removeColor case .colorDescription: return .colorDescription case .backgroundEmojiHeader: @@ -84,14 +95,18 @@ private enum PeerNameColorScreenEntry: ItemListNodeEntry { return 0 case .colorMessage: return 1 - case .colorPicker: + case .colorProfile: return 2 - case .colorDescription: + case .colorPicker: return 3 - case .backgroundEmojiHeader: + case .removeColor: return 4 - case .backgroundEmoji: + case .colorDescription: return 5 + case .backgroundEmojiHeader: + return 6 + case .backgroundEmoji: + return 7 } } @@ -109,8 +124,29 @@ private enum PeerNameColorScreenEntry: ItemListNodeEntry { } else { return false } - case let .colorPicker(lhsColors, lhsCurrentColor): - if case let .colorPicker(rhsColors, rhsCurrentColor) = rhs, lhsColors == rhsColors, lhsCurrentColor == rhsCurrentColor { + case let .colorProfile(lhsPeer, lhsFiles, lhsNameDisplayOrder): + if case let .colorProfile(rhsPeer, rhsFiles, rhsNameDisplayOrder) = rhs { + if lhsPeer != rhsPeer { + return false + } + if lhsFiles != rhsFiles { + return false + } + if lhsNameDisplayOrder != rhsNameDisplayOrder { + return false + } + return true + } else { + return false + } + case let .colorPicker(lhsColors, lhsCurrentColor, lhsIsProfile): + if case let .colorPicker(rhsColors, rhsCurrentColor, rhsIsProfile) = rhs, lhsColors == rhsColors, lhsCurrentColor == rhsCurrentColor, lhsIsProfile == rhsIsProfile { + return true + } else { + return false + } + case .removeColor: + if case .removeColor = rhs { return true } else { return false @@ -127,8 +163,8 @@ private enum PeerNameColorScreenEntry: ItemListNodeEntry { } else { return false } - case let .backgroundEmoji(lhsEmojiContent, lhsBackgroundIconColor): - if case let .backgroundEmoji(rhsEmojiContent, rhsBackgroundIconColor) = rhs, lhsEmojiContent == rhsEmojiContent, lhsBackgroundIconColor == rhsBackgroundIconColor { + case let .backgroundEmoji(lhsEmojiContent, lhsBackgroundIconColor, lhsIsProfile, lhsHasRemoveButton): + if case let .backgroundEmoji(rhsEmojiContent, rhsBackgroundIconColor, rhsIsProfile, rhsHasRemoveButton) = rhs, lhsEmojiContent == rhsEmojiContent, lhsBackgroundIconColor == rhsBackgroundIconColor, lhsIsProfile == rhsIsProfile, lhsHasRemoveButton == rhsHasRemoveButton { return true } else { return false @@ -157,25 +193,42 @@ private enum PeerNameColorScreenEntry: ItemListNodeEntry { wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, - messageItems: items) - case let .colorPicker(colors, currentColor): + messageItems: items + ) + case let .colorProfile(peer, files, nameDisplayOrder): + return PeerNameColorProfilePreviewItem( + context: arguments.context, + theme: presentationData.theme, + componentTheme: presentationData.theme, + strings: presentationData.strings, + sectionId: self.section, + peer: peer, + files: files, + nameDisplayOrder: nameDisplayOrder + ) + case let .colorPicker(colors, currentColor, isProfile): return PeerNameColorItem( theme: presentationData.theme, colors: colors, + isProfile: isProfile, currentColor: currentColor, updated: { color in arguments.updateNameColor(color) }, sectionId: self.section ) + case .removeColor: + return ItemListActionItem(presentationData: presentationData, title: presentationData.strings.ProfileColorSetup_ResetAction, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { + arguments.resetColor() + }) case let .colorDescription(text): return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) case let .backgroundEmojiHeader(text, action): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, actionText: action, action: action != nil ? { - arguments.updateBackgroundEmojiId(0) + arguments.updateBackgroundEmojiId(0, nil) } : nil, sectionId: self.section) - case let .backgroundEmoji(emojiContent, backgroundIconColor): - return EmojiPickerItem(context: arguments.context, theme: presentationData.theme, strings: presentationData.strings, emojiContent: emojiContent, backgroundIconColor: backgroundIconColor, sectionId: self.section) + case let .backgroundEmoji(emojiContent, backgroundIconColor, isProfileColor, hasRemoveButton): + return EmojiPickerItem(context: arguments.context, theme: presentationData.theme, strings: presentationData.strings, emojiContent: emojiContent, backgroundIconColor: backgroundIconColor, isProfileColor: isProfileColor, hasRemoveButton: hasRemoveButton, sectionId: self.section) } } } @@ -185,6 +238,15 @@ private struct PeerNameColorScreenState: Equatable { var updatedBackgroundEmojiId: Int64? var inProgress: Bool = false var needsBoosts: Bool = false + + var updatedProfileColor: PeerNameColor? + var hasUpdatedProfileColor: Bool = false + var updatedProfileBackgroundEmojiId: Int64? + var hasUpdatedProfileBackgroundEmojiId: Bool = false + + var selectedTabIndex: Int = 0 + + var files: [Int64: TelegramMediaFile] = [:] } private func peerNameColorScreenEntries( @@ -222,6 +284,20 @@ private func peerNameColorScreenEntries( backgroundEmojiId = nil } + let profileColor: PeerNameColor? + if state.hasUpdatedProfileColor { + profileColor = state.updatedProfileColor + } else { + profileColor = peer.profileColor + } + var selectedProfileEmojiId: Int64? + if state.hasUpdatedProfileBackgroundEmojiId { + selectedProfileEmojiId = state.updatedProfileBackgroundEmojiId + } else { + selectedProfileEmojiId = peer.profileBackgroundEmojiId + } + let profileColors = profileColor.flatMap { profileColor in nameColors.getProfile(profileColor, dark: presentationData.theme.overallDarkAppearance) } + let replyText: String let messageText: String if case .channel = peer { @@ -242,27 +318,86 @@ private func peerNameColorScreenEntries( linkPreview: (presentationData.strings.NameColor_ChatPreview_LinkSite, presentationData.strings.NameColor_ChatPreview_LinkTitle, presentationData.strings.NameColor_ChatPreview_LinkText), text: messageText ) - entries.append(.colorMessage( - wallpaper: presentationData.chatWallpaper, - fontSize: presentationData.chatFontSize, - bubbleCorners: presentationData.chatBubbleCorners, - dateTimeFormat: presentationData.dateTimeFormat, - nameDisplayOrder: presentationData.nameDisplayOrder, - items: [messageItem] - )) - entries.append(.colorPicker( - colors: nameColors, - currentColor: nameColor - )) - if case .channel = peer { - entries.append(.colorDescription(presentationData.strings.NameColor_ChatPreview_Description_Channel)) + if state.selectedTabIndex == 0 { + entries.append(.colorMessage( + wallpaper: presentationData.chatWallpaper, + fontSize: presentationData.chatFontSize, + bubbleCorners: presentationData.chatBubbleCorners, + dateTimeFormat: presentationData.dateTimeFormat, + nameDisplayOrder: presentationData.nameDisplayOrder, + items: [messageItem] + )) + } else { + var updatedPeer = peer + switch updatedPeer { + case let .user(user): + updatedPeer = .user(user.withUpdatedNameColor(nameColor).withUpdatedBackgroundEmojiId(backgroundEmojiId).withUpdatedProfileColor(profileColor).withUpdatedProfileBackgroundEmojiId(selectedProfileEmojiId)) + case let .channel(channel): + updatedPeer = .channel(channel.withUpdatedNameColor(nameColor).withUpdatedBackgroundEmojiId(backgroundEmojiId).withUpdatedProfileColor(profileColor).withUpdatedProfileBackgroundEmojiId(selectedProfileEmojiId)) + default: + break + } + var files: [Int64: TelegramMediaFile] = [:] + if let fileId = updatedPeer.profileBackgroundEmojiId, let file = state.files[fileId] { + files[fileId] = file + } + entries.append(.colorProfile( + peer: updatedPeer, + files: files, + nameDisplayOrder: presentationData.nameDisplayOrder + )) + } + if state.selectedTabIndex == 0 { + entries.append(.colorPicker( + colors: nameColors, + currentColor: nameColor, + isProfile: false + )) } else { - entries.append(.colorDescription(presentationData.strings.NameColor_ChatPreview_Description_Account)) + entries.append(.colorPicker( + colors: nameColors, + currentColor: profileColor, + isProfile: true + )) + } + if state.selectedTabIndex == 1 && profileColor != nil { + entries.append(.removeColor) } - if let emojiContent { - entries.append(.backgroundEmojiHeader(presentationData.strings.NameColor_BackgroundEmoji_Title, backgroundEmojiId != nil ? presentationData.strings.NameColor_BackgroundEmoji_Remove : nil)) - entries.append(.backgroundEmoji(emojiContent, colors.main)) + if state.selectedTabIndex == 0 { + if case .channel = peer { + entries.append(.colorDescription(presentationData.strings.NameColor_ChatPreview_Description_Channel)) + } else { + entries.append(.colorDescription(presentationData.strings.NameColor_ChatPreview_Description_Account)) + } + + if let emojiContent { + var selectedItems = Set() + if let backgroundEmojiId { + selectedItems.insert(MediaId(namespace: Namespaces.Media.CloudFile, id: backgroundEmojiId)) + } + let emojiContent = emojiContent.withSelectedItems(selectedItems).withCustomTintColor(colors.main) + + entries.append(.backgroundEmojiHeader(presentationData.strings.NameColor_BackgroundEmoji_Title, (backgroundEmojiId != nil && backgroundEmojiId != 0) ? presentationData.strings.NameColor_BackgroundEmoji_Remove : nil)) + entries.append(.backgroundEmoji(emojiContent, colors.main, false, false)) + } + } else { + if let emojiContent { + var selectedItems = Set() + if let selectedProfileEmojiId { + selectedItems.insert(MediaId(namespace: Namespaces.Media.CloudFile, id: selectedProfileEmojiId)) + } + let emojiContent = emojiContent.withSelectedItems(selectedItems).withCustomTintColor(profileColors?.main ?? presentationData.theme.list.itemSecondaryTextColor) + + entries.append(.backgroundEmojiHeader(presentationData.strings.ProfileColorSetup_IconSectionTitle, (selectedProfileEmojiId != nil && selectedProfileEmojiId != 0) ? presentationData.strings.NameColor_BackgroundEmoji_Remove : nil)) + entries.append(.backgroundEmoji(emojiContent, profileColors?.main ?? presentationData.theme.list.itemSecondaryTextColor, true, profileColor != nil)) + } else { + if case .channel = peer { + entries.append(.colorDescription(presentationData.strings.ProfileColorSetup_ChannelColorInfoLabel)) + } else { + entries.append(.colorDescription(presentationData.strings.ProfileColorSetup_AccountColorInfoLabel)) + } + } } } @@ -300,14 +435,43 @@ public func PeerNameColorScreen( updateNameColor: { color in updateState { state in var updatedState = state - updatedState.updatedNameColor = color + + if state.selectedTabIndex == 0 { + if let color { + updatedState.updatedNameColor = color + } + } else { + updatedState.updatedProfileColor = color + updatedState.hasUpdatedProfileColor = true + } + return updatedState + } + }, + updateBackgroundEmojiId: { emojiId, file in + updateState { state in + var updatedState = state + if state.selectedTabIndex == 0 { + updatedState.updatedBackgroundEmojiId = emojiId + } else { + updatedState.hasUpdatedProfileBackgroundEmojiId = true + updatedState.updatedProfileBackgroundEmojiId = emojiId + } + if let file { + updatedState.files[file.fileId.id] = file + } return updatedState } }, - updateBackgroundEmojiId: { emojiId in + resetColor: { updateState { state in var updatedState = state - updatedState.updatedBackgroundEmojiId = emojiId + + if state.selectedTabIndex == 1 { + updatedState.updatedProfileColor = nil + updatedState.hasUpdatedProfileColor = true + updatedState.updatedProfileBackgroundEmojiId = nil + updatedState.hasUpdatedProfileBackgroundEmojiId = true + } return updatedState } } @@ -321,12 +485,24 @@ public func PeerNameColorScreen( peerId = channelId } - let emojiContent = combineLatest( - context.sharedContext.presentationData, - statePromise.get(), - context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) + let emojiContent = EmojiPagerContentComponent.emojiInputData( + context: context, + animationCache: context.animationCache, + animationRenderer: context.animationRenderer, + isStandalone: false, + subject: .backgroundIcon, + hasTrending: false, + topReactionItems: [], + areUnicodeEmojiEnabled: false, + areCustomEmojiEnabled: true, + chatPeerId: context.account.peerId, + selectedItems: Set(), + backgroundIconColor: nil ) - |> mapToSignal { presentationData, state, peer -> Signal in + /*let emojiContent: Signal = combineLatest( + context.sharedContext.presentationData + ) + |> mapToSignal { presentationData, state, peer -> Signal<(EmojiPagerContentComponent, EmojiPagerContentComponent), NoError> in var selectedEmojiId: Int64? if let updatedBackgroundEmojiId = state.updatedBackgroundEmojiId { selectedEmojiId = updatedBackgroundEmojiId @@ -339,7 +515,22 @@ public func PeerNameColorScreen( } else { nameColor = (peer?.nameColor ?? .blue) } + + var selectedProfileEmojiId: Int64? + if state.hasUpdatedProfileBackgroundEmojiId { + selectedProfileEmojiId = state.updatedProfileBackgroundEmojiId + } else { + selectedProfileEmojiId = peer?.profileBackgroundEmojiId + } + let profileColor: PeerNameColor? + if state.hasUpdatedProfileColor { + profileColor = state.updatedProfileColor + } else { + profileColor = peer?.profileColor + } + let color = context.peerNameColors.get(nameColor, dark: presentationData.theme.overallDarkAppearance) + let profileColorValue: UIColor? = profileColor.flatMap { profileColor in context.peerNameColors.getProfile(profileColor, dark: presentationData.theme.overallDarkAppearance).main } let selectedItems: [EngineMedia.Id] if let selectedEmojiId, selectedEmojiId != 0 { @@ -348,21 +539,13 @@ public func PeerNameColorScreen( selectedItems = [] } - return EmojiPagerContentComponent.emojiInputData( - context: context, - animationCache: context.animationCache, - animationRenderer: context.animationRenderer, - isStandalone: false, - subject: .backgroundIcon, - hasTrending: false, - topReactionItems: [], - areUnicodeEmojiEnabled: false, - areCustomEmojiEnabled: true, - chatPeerId: context.account.peerId, - selectedItems: Set(selectedItems), - backgroundIconColor: color.main - ) - } + let selectedProfileItems: [EngineMedia.Id] + if let selectedProfileEmojiId, selectedProfileEmojiId != 0 { + selectedProfileItems = [EngineMedia.Id(namespace: Namespaces.Media.CloudFile, id: selectedProfileEmojiId)] + } else { + selectedProfileItems = [] + } + }*/ let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData let signal = combineLatest(queue: .mainQueue(), @@ -375,17 +558,15 @@ public func PeerNameColorScreen( |> deliverOnMainQueue |> map { presentationData, state, availableReactions, peer, emojiContent -> (ItemListControllerState, (ItemListNodeState, Any)) in let isPremium = peer?.isPremium ?? false - let title: String let buttonTitle: String let isLocked: Bool switch subject { case .account: - title = presentationData.strings.NameColor_Title_Account isLocked = !isPremium case .channel: - title = presentationData.strings.NameColor_Title_Channel isLocked = false } + let _ = isLocked let backgroundEmojiId: Int64 if let updatedBackgroundEmojiId = state.updatedBackgroundEmojiId { @@ -400,8 +581,9 @@ public func PeerNameColorScreen( } else { buttonTitle = presentationData.strings.NameColor_ApplyColor } + let _ = buttonTitle - let footerItem = ApplyColorFooterItem( + /*let footerItem = ApplyColorFooterItem( theme: presentationData.theme, title: buttonTitle, locked: isLocked, @@ -432,17 +614,19 @@ public func PeerNameColorScreen( presentImpl?(controller) } } - ) + )*/ emojiContent.inputInteractionHolder.inputInteraction = EmojiPagerContentComponent.InputInteraction( performItemAction: { _, item, _, _, _, _ in var selectedFileId: Int64? + var selectedFile: TelegramMediaFile? if let fileId = item.itemFile?.fileId.id { selectedFileId = fileId + selectedFile = item.itemFile } else { selectedFileId = 0 } - arguments.updateBackgroundEmojiId(selectedFileId) + arguments.updateBackgroundEmojiId(selectedFileId, selectedFile) }, deleteBackwards: { }, @@ -512,11 +696,50 @@ public func PeerNameColorScreen( emojiContent: emojiContent ) + let title: ItemListControllerTitle + if case .user = peer { + title = .sectionControl([presentationData.strings.ProfileColorSetup_TitleName, presentationData.strings.ProfileColorSetup_TitleProfile], state.selectedTabIndex) + } else { + title = .text(presentationData.strings.ProfileColorSetup_TitleChannelColor) + } + let controllerState = ItemListControllerState( presentationData: ItemListPresentationData(presentationData), - title: .text(title), + title: title, leftNavigationButton: nil, - rightNavigationButton: nil, + rightNavigationButton: ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: { + if !isLocked { + applyChangesImpl?() + } else { + HapticFeedback().impact(.light) + let controller = UndoOverlayController( + presentationData: presentationData, + content: .premiumPaywall( + title: nil, + text: presentationData.strings.NameColor_TooltipPremium_Account, + customUndoText: nil, + timeout: nil, + linkAction: nil + ), + elevatedLayout: false, + action: { action in + if case .info = action { + var replaceImpl: ((ViewController) -> Void)? + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .colors, action: { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false, dismissed: nil) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + pushImpl?(controller) + } + return true + } + ) + presentImpl?(controller) + } + }), backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false ) @@ -524,7 +747,7 @@ public func PeerNameColorScreen( presentationData: ItemListPresentationData(presentationData), entries: entries, style: .blocks, - footerItem: footerItem, + footerItem: nil, animateChanges: false ) @@ -536,6 +759,13 @@ public func PeerNameColorScreen( let controller = ItemListController(context: context, state: signal) controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) + controller.titleControlValueChanged = { value in + updateState { state in + var state = state + state.selectedTabIndex = value + return state + } + } presentImpl = { [weak controller] c in guard let controller else { return @@ -596,7 +826,7 @@ public func PeerNameColorScreen( return } let state = stateValue.with { $0 } - if state.updatedNameColor == nil && state.updatedBackgroundEmojiId == nil { + if state.updatedNameColor == nil && state.updatedBackgroundEmojiId == nil && !state.hasUpdatedProfileColor && !state.hasUpdatedProfileBackgroundEmojiId { dismissImpl?() return } @@ -607,14 +837,27 @@ public func PeerNameColorScreen( let backgroundEmojiId = state.updatedBackgroundEmojiId ?? peer.backgroundEmojiId let colors = context.peerNameColors.get(nameColor ?? .blue, dark: presentationData.theme.overallDarkAppearance) + let profileColor = state.hasUpdatedProfileColor ? state.updatedProfileColor : peer.profileColor + let profileBackgroundEmojiId = state.hasUpdatedProfileBackgroundEmojiId ? state.updatedProfileBackgroundEmojiId : peer.profileBackgroundEmojiId + switch subject { case .account: - let _ = context.engine.accountData.updateNameColorAndEmoji(nameColor: nameColor ?? .blue, backgroundEmojiId: backgroundEmojiId ?? 0).startStandalone() + let _ = context.engine.accountData.updateNameColorAndEmoji(nameColor: nameColor ?? .blue, backgroundEmojiId: backgroundEmojiId ?? 0, profileColor: profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId ?? 0).startStandalone() if let navigationController = controller?.navigationController as? NavigationController { Queue.mainQueue().after(0.25) { if let lastController = navigationController.viewControllers.last as? ViewController { - let tipController = UndoOverlayController(presentationData: presentationData, content: .image(image: generatePeerNameColorImage(nameColor: colors, isDark: presentationData.theme.overallDarkAppearance, bounds: CGSize(width: 32.0, height: 32.0), size: CGSize(width: 22.0, height: 22.0))!, title: nil, text: presentationData.strings.NameColor_YourColorUpdated, round: false, undoText: nil), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in return false }) + var colorList: [PeerNameColors.Colors] = [] + if let nameColor { + colorList.append(context.peerNameColors.get(nameColor, dark: presentationData.theme.overallDarkAppearance)) + } + if let profileColor { + colorList.append(context.peerNameColors.getProfile(profileColor, dark: presentationData.theme.overallDarkAppearance, subject: .palette)) + } + + let colorImage = generateSettingsMenuPeerColorsLabelIcon(colors: colorList) + + let tipController = UndoOverlayController(presentationData: presentationData, content: .image(image: colorImage, title: nil, text: presentationData.strings.ProfileColorSetup_ToastAccountColorUpdated, round: false, undoText: nil), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in return false }) lastController.present(tipController, in: .window(.root)) } } @@ -627,7 +870,7 @@ public func PeerNameColorScreen( updatedState.inProgress = true return updatedState } - let _ = (context.engine.peers.updatePeerNameColorAndEmoji(peerId: peerId, nameColor: nameColor ?? .blue, backgroundEmojiId: backgroundEmojiId ?? 0) + let _ = (context.engine.peers.updatePeerNameColorAndEmoji(peerId: peerId, nameColor: nameColor ?? .blue, backgroundEmojiId: backgroundEmojiId ?? 0, profileColor: profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId ?? 0) |> deliverOnMainQueue).startStandalone(next: { }, error: { error in if case .channelBoostRequired = error { diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreenComponent.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreenComponent.swift new file mode 100644 index 00000000000..997b96aaa88 --- /dev/null +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreenComponent.swift @@ -0,0 +1,13 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import SwiftSignalKit +import Postbox +import TelegramCore +import TelegramPresentationData +import TelegramUIPreferences +import PresentationDataUtils +import AccountContext +import UndoUI + diff --git a/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/BUILD b/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/BUILD new file mode 100644 index 00000000000..e35ab3233bd --- /dev/null +++ b/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/BUILD @@ -0,0 +1,36 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "QuickReactionSetupController", + module_name = "QuickReactionSetupController", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AccountContext", + "//submodules/TelegramUI/Components/AnimationCache", + "//submodules/AsyncDisplayKit", + "//submodules/ComponentFlow", + "//submodules/Display", + "//submodules/TelegramUI/Components/EmojiStatusComponent", + "//submodules/TelegramUI/Components/EmojiStatusSelectionComponent", + "//submodules/TelegramUI/Components/EntityKeyboard", + "//submodules/ItemListUI", + "//submodules/Postbox", + "//submodules/PresentationDataUtils", + "//submodules/Components/ReactionImageComponent", + "//submodules/ReactionSelectionNode", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/TelegramCore", + "//submodules/TelegramPresentationData", + "//submodules/TelegramUIPreferences", + "//submodules/WallpaperBackgroundNode", + "//submodules/WebPBinding", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/SettingsUI/Sources/Reactions/ItemListReactionItem.swift b/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ItemListReactionItem.swift similarity index 100% rename from submodules/SettingsUI/Sources/Reactions/ItemListReactionItem.swift rename to submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ItemListReactionItem.swift diff --git a/submodules/SettingsUI/Sources/Reactions/QuickReactionSetupController.swift b/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/QuickReactionSetupController.swift similarity index 93% rename from submodules/SettingsUI/Sources/Reactions/QuickReactionSetupController.swift rename to submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/QuickReactionSetupController.swift index 5c3ddeec9e1..da5694a1f65 100644 --- a/submodules/SettingsUI/Sources/Reactions/QuickReactionSetupController.swift +++ b/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/QuickReactionSetupController.swift @@ -46,7 +46,7 @@ private enum QuickReactionSetupControllerEntry: ItemListNodeEntry { } case demoHeader(String) - case demoMessage(wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, availableReactions: AvailableReactions?, reaction: MessageReaction.Reaction?) + case demoMessage(wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, availableReactions: AvailableReactions?, reaction: MessageReaction.Reaction?, accountPeer: Peer?) case demoDescription(String) case quickReaction(String, MessageReaction.Reaction, AvailableReactions) case quickReactionDescription(String) @@ -98,8 +98,8 @@ private enum QuickReactionSetupControllerEntry: ItemListNodeEntry { } else { return false } - case let .demoMessage(lhsWallpaper, lhsFontSize, lhsBubbleCorners, lhsDateTimeFormat, lhsNameDisplayOrder, lhsAvailableReactions, lhsReaction): - if case let .demoMessage(rhsWallpaper, rhsFontSize, rhsBubbleCorners, rhsDateTimeFormat, rhsNameDisplayOrder, rhsAvailableReactions, rhsReaction) = rhs, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsBubbleCorners == rhsBubbleCorners, lhsDateTimeFormat == rhsDateTimeFormat, lhsNameDisplayOrder == rhsNameDisplayOrder, lhsAvailableReactions == rhsAvailableReactions, lhsReaction == rhsReaction { + case let .demoMessage(lhsWallpaper, lhsFontSize, lhsBubbleCorners, lhsDateTimeFormat, lhsNameDisplayOrder, lhsAvailableReactions, lhsReaction, lhsAccountPeer): + if case let .demoMessage(rhsWallpaper, rhsFontSize, rhsBubbleCorners, rhsDateTimeFormat, rhsNameDisplayOrder, rhsAvailableReactions, rhsReaction, rhsAccountPeer) = rhs, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsBubbleCorners == rhsBubbleCorners, lhsDateTimeFormat == rhsDateTimeFormat, lhsNameDisplayOrder == rhsNameDisplayOrder, lhsAvailableReactions == rhsAvailableReactions, lhsReaction == rhsReaction, lhsAccountPeer?.id == rhsAccountPeer?.id { return true } else { return false @@ -134,7 +134,7 @@ private enum QuickReactionSetupControllerEntry: ItemListNodeEntry { switch self { case let .demoHeader(text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) - case let .demoMessage(wallpaper, fontSize, chatBubbleCorners, dateTimeFormat, nameDisplayOrder, availableReactions, reaction): + case let .demoMessage(wallpaper, fontSize, chatBubbleCorners, dateTimeFormat, nameDisplayOrder, availableReactions, reaction, accountPeer): return ReactionChatPreviewItem( context: arguments.context, theme: presentationData.theme, @@ -147,6 +147,7 @@ private enum QuickReactionSetupControllerEntry: ItemListNodeEntry { nameDisplayOrder: nameDisplayOrder, availableReactions: availableReactions, reaction: reaction, + accountPeer: accountPeer, toggleReaction: { arguments.toggleReaction() } @@ -172,7 +173,8 @@ private func quickReactionSetupControllerEntries( availableReactions: AvailableReactions?, reactionSettings: ReactionSettings, state: QuickReactionSetupControllerState, - isPremium: Bool + isPremium: Bool, + accountPeer: Peer? ) -> [QuickReactionSetupControllerEntry] { var entries: [QuickReactionSetupControllerEntry] = [] @@ -185,7 +187,8 @@ private func quickReactionSetupControllerEntries( dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, availableReactions: availableReactions, - reaction: state.hasReaction ? reactionSettings.effectiveQuickReaction(hasPremium: isPremium) : nil + reaction: state.hasReaction ? reactionSettings.effectiveQuickReaction(hasPremium: isPremium) : nil, + accountPeer: accountPeer )) entries.append(.demoDescription(presentationData.strings.Settings_QuickReactionSetup_DemoInfo)) @@ -257,7 +260,8 @@ public func quickReactionSetupController( availableReactions: availableReactions, reactionSettings: settings, state: state, - isPremium: isPremium + isPremium: isPremium, + accountPeer: accountPeer?._asPeer() ) let controllerState = ItemListControllerState( @@ -283,16 +287,7 @@ public func quickReactionSetupController( let controller = ItemListController(context: context, state: signal) - controller.didScrollWithOffset = { [weak controller] offset, transition, _, _ in - guard let controller = controller else { - return - } - controller.forEachItemNode { itemNode in - if let itemNode = itemNode as? ReactionChatPreviewItemNode { - itemNode.standaloneReactionAnimation?.addRelativeContentOffset(CGPoint(x: 0.0, y: offset), transition: transition) - } - } - } + controller.alwaysSynchronous = true openQuickReactionImpl = { [weak controller] in let _ = (combineLatest(queue: .mainQueue(), diff --git a/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift b/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift similarity index 85% rename from submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift rename to submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift index 33ffc6a2fc7..603c48c9ac1 100644 --- a/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift +++ b/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift @@ -26,9 +26,10 @@ class ReactionChatPreviewItem: ListViewItem, ItemListItem { let nameDisplayOrder: PresentationPersonNameOrder let availableReactions: AvailableReactions? let reaction: MessageReaction.Reaction? + let accountPeer: Peer? let toggleReaction: () -> Void - init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, wallpaper: TelegramWallpaper, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, availableReactions: AvailableReactions?, reaction: MessageReaction.Reaction?, toggleReaction: @escaping () -> Void) { + init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, wallpaper: TelegramWallpaper, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, availableReactions: AvailableReactions?, reaction: MessageReaction.Reaction?, accountPeer: Peer?, toggleReaction: @escaping () -> Void) { self.context = context self.theme = theme self.strings = strings @@ -40,6 +41,7 @@ class ReactionChatPreviewItem: ListViewItem, ItemListItem { self.nameDisplayOrder = nameDisplayOrder self.availableReactions = availableReactions self.reaction = reaction + self.accountPeer = accountPeer self.toggleReaction = toggleReaction } @@ -53,7 +55,7 @@ class ReactionChatPreviewItem: ListViewItem, ItemListItem { Queue.mainQueue().async { completion(node, { - return (nil, { _ in apply() }) + return (nil, { _ in apply(.None) }) }) } } @@ -68,7 +70,7 @@ class ReactionChatPreviewItem: ListViewItem, ItemListItem { let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) Queue.mainQueue().async { completion(layout, { _ in - apply() + apply(animation) }) } } @@ -83,6 +85,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode { private let bottomStripeNode: ASDisplayNode private let maskNode: ASImageNode + private let clippingNode: ASDisplayNode private let containerNode: ASDisplayNode private var messageNode: ListViewItemNode? @@ -104,14 +107,17 @@ class ReactionChatPreviewItemNode: ListViewItemNode { self.maskNode = ASImageNode() + self.clippingNode = ASDisplayNode() + self.clippingNode.clipsToBounds = true + self.clippingNode.layer.cornerRadius = 10.0 + self.containerNode = ASDisplayNode() self.containerNode.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) super.init(layerBacked: false, dynamicBounce: false) - self.clipsToBounds = true - - self.addSubnode(self.containerNode) + self.addSubnode(self.clippingNode) + self.clippingNode.addSubnode(self.containerNode) } deinit { @@ -227,32 +233,30 @@ class ReactionChatPreviewItemNode: ListViewItemNode { self.standaloneReactionAnimation = nil } - if let supernode = self.supernode { - let standaloneReactionAnimation = StandaloneReactionAnimation(genericReactionEffect: self.genericReactionEffect) - self.loadNextGenericReactionEffect(context: item.context) - self.standaloneReactionAnimation = standaloneReactionAnimation - - let animationCache = item.context.animationCache - - supernode.addSubnode(standaloneReactionAnimation) - standaloneReactionAnimation.frame = supernode.bounds - standaloneReactionAnimation.animateReactionSelection( - context: item.context, theme: item.theme, animationCache: animationCache, reaction: reactionItem, - avatarPeers: [], - playHaptic: false, - isLarge: false, - targetView: targetView, - addStandaloneReactionAnimation: nil, - completion: { [weak standaloneReactionAnimation] in - standaloneReactionAnimation?.removeFromSupernode() - } - ) - } + let standaloneReactionAnimation = StandaloneReactionAnimation(genericReactionEffect: self.genericReactionEffect) + self.loadNextGenericReactionEffect(context: item.context) + self.standaloneReactionAnimation = standaloneReactionAnimation + + let animationCache = item.context.animationCache + + self.addSubnode(standaloneReactionAnimation) + standaloneReactionAnimation.frame = self.bounds + standaloneReactionAnimation.animateReactionSelection( + context: item.context, theme: item.theme, animationCache: animationCache, reaction: reactionItem, + avatarPeers: [], + playHaptic: false, + isLarge: false, + targetView: targetView, + addStandaloneReactionAnimation: nil, + completion: { [weak standaloneReactionAnimation] in + standaloneReactionAnimation?.removeFromSupernode() + } + ) } } } - func asyncLayout() -> (_ item: ReactionChatPreviewItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { + func asyncLayout() -> (_ item: ReactionChatPreviewItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation) -> Void) { let currentNode = self.messageNode let previousItem = self.item @@ -261,7 +265,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode { return { item, params, neighbors in if currentBackgroundNode == nil { currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false) - currentBackgroundNode?.update(wallpaper: item.wallpaper) + currentBackgroundNode?.update(wallpaper: item.wallpaper, animated: false) currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) } @@ -274,21 +278,26 @@ class ReactionChatPreviewItemNode: ListViewItemNode { var peers = SimpleDictionary() let messages = SimpleDictionary() - peers[userPeerId] = TelegramUser(id: userPeerId, accessHash: nil, firstName: item.strings.Settings_QuickReactionSetup_DemoMessageAuthor, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil) + peers[userPeerId] = TelegramUser(id: userPeerId, accessHash: nil, firstName: item.strings.Settings_QuickReactionSetup_DemoMessageAuthor, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) let messageText = item.strings.Settings_QuickReactionSetup_DemoMessageText var attributes: [MessageAttribute] = [] if let reaction = item.reaction { - attributes.append(ReactionsMessageAttribute(canViewList: false, reactions: [MessageReaction(value: reaction, count: 1, chosenOrder: 0)], recentPeers: [])) + var recentPeers: [ReactionsMessageAttribute.RecentPeer] = [] + if let accountPeer = item.accountPeer { + recentPeers.append(ReactionsMessageAttribute.RecentPeer(value: reaction, isLarge: false, isUnseen: false, isMy: true, peerId: accountPeer.id, timestamp: nil)) + peers[accountPeer.id] = accountPeer + } + attributes.append(ReactionsMessageAttribute(canViewList: false, reactions: [MessageReaction(value: reaction, count: 1, chosenOrder: 0)], recentPeers: recentPeers)) } - let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: chatPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[userPeerId], text: messageText, attributes: attributes, media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: item.availableReactions, isCentered: true) + let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: chatPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[userPeerId], text: messageText, attributes: attributes, media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: item.availableReactions, accountPeer: item.accountPeer, isCentered: true) var node: ListViewItemNode? if let current = currentNode { node = current - messageItem.updateNode(async: { $0() }, node: { return current }, params: params, previousItem: nil, nextItem: nil, animation: .System(duration: 0.3, transition: ControlledTransition(duration: 0.3, curve: .easeInOut, interactive: false)), completion: { (layout, apply) in + messageItem.updateNode(async: { $0() }, node: { return current }, params: params, previousItem: nil, nextItem: nil, animation: .System(duration: 0.4, transition: ControlledTransition(duration: 0.4, curve: .spring, interactive: false)), completion: { (layout, apply) in let nodeFrame = CGRect(origin: current.frame.origin, size: CGSize(width: layout.size.width, height: layout.size.height)) current.contentSize = layout.contentSize @@ -300,6 +309,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode { } else { messageItem.nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: false, previousItem: nil, nextItem: nil, completion: { messageNode, apply in node = messageNode + apply().1(ListViewItemApply(isOnScreen: true)) }) node?.isUserInteractionEnabled = false @@ -310,14 +320,14 @@ class ReactionChatPreviewItemNode: ListViewItemNode { contentSize.height += node.frame.size.height } if item.reaction == nil { - contentSize.height += 34.0 + //contentSize.height += 34.0 } insets = itemListNeighborsGroupedInsets(neighbors, params) let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) let layoutSize = layout.size - return (layout, { [weak self] in + return (layout, { [weak self] animation in if let strongSelf = self { if let previousItem = strongSelf.item, previousItem.reaction != item.reaction { if let standaloneReactionAnimation = strongSelf.standaloneReactionAnimation { @@ -331,7 +341,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode { strongSelf.item = item if let currentBackgroundNode { - currentBackgroundNode.update(wallpaper: item.wallpaper) + currentBackgroundNode.update(wallpaper: item.wallpaper, animated: false) currentBackgroundNode.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) } @@ -341,7 +351,9 @@ class ReactionChatPreviewItemNode: ListViewItemNode { strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize) - var topOffset: CGFloat = 16.0 + 17.0 + animation.animator.updateFrame(layer: strongSelf.clippingNode.layer, frame: CGRect(origin: CGPoint(), size: contentSize), completion: nil) + + var topOffset: CGFloat = 16.0 if let node = node { strongSelf.messageNode = node if node.supernode == nil { @@ -357,17 +369,17 @@ class ReactionChatPreviewItemNode: ListViewItemNode { if let currentBackgroundNode = currentBackgroundNode, strongSelf.backgroundNode !== currentBackgroundNode { strongSelf.backgroundNode = currentBackgroundNode - strongSelf.insertSubnode(currentBackgroundNode, at: 0) + strongSelf.clippingNode.insertSubnode(currentBackgroundNode, at: 0) } if strongSelf.topStripeNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1) + strongSelf.clippingNode.insertSubnode(strongSelf.topStripeNode, at: 1) } if strongSelf.bottomStripeNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) + strongSelf.clippingNode.insertSubnode(strongSelf.bottomStripeNode, at: 2) } if strongSelf.maskNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.maskNode, at: 3) + strongSelf.addSubnode(strongSelf.maskNode) } let hasCorners = itemListHasRoundedBlockLayout(params) var hasTopCorners = false @@ -413,13 +425,14 @@ class ReactionChatPreviewItemNode: ListViewItemNode { } if let backgroundNode = strongSelf.backgroundNode { - backgroundNode.frame = backgroundFrame.insetBy(dx: 0.0, dy: -100.0) - backgroundNode.update(wallpaper: item.wallpaper) + backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: backgroundFrame.width, height: 500.0)).insetBy(dx: 0.0, dy: -100.0) + backgroundNode.update(wallpaper: item.wallpaper, animated: false) backgroundNode.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) backgroundNode.updateLayout(size: backgroundNode.bounds.size, displayMode: displayMode, transition: .immediate) } - strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0) + animation.animator.updateFrame(layer: strongSelf.maskNode.layer, frame: backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0), completion: nil) + strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/PieChartComponent.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/PieChartComponent.swift index 0ebd20b6f7b..4878d848c53 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/PieChartComponent.swift +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/PieChartComponent.swift @@ -1054,23 +1054,7 @@ final class PieChartComponent: Component { self.backgroundColor = nil self.isOpaque = false - var previousTimestamp: Double? - self.displayLink = SharedDisplayLinkDriver.shared.add(needsHighestFramerate: true, { [weak self] in - let timestamp = CACurrentMediaTime() - var delta: Double - if let previousTimestamp { - delta = timestamp - previousTimestamp - } else { - delta = 1.0 / 60.0 - } - previousTimestamp = timestamp - - if delta < 0.0 { - delta = 1.0 / 60.0 - } else if delta > 0.5 { - delta = 1.0 / 60.0 - } - + self.displayLink = SharedDisplayLinkDriver.shared.add(framesPerSecond: .max, { [weak self] delta in self?.update(deltaTime: CGFloat(delta)) }) } diff --git a/submodules/TelegramUI/Components/Stories/ForwardInfoPanelComponent/BUILD b/submodules/TelegramUI/Components/Stories/ForwardInfoPanelComponent/BUILD new file mode 100644 index 00000000000..f17f6090055 --- /dev/null +++ b/submodules/TelegramUI/Components/Stories/ForwardInfoPanelComponent/BUILD @@ -0,0 +1,22 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ForwardInfoPanelComponent", + module_name = "ForwardInfoPanelComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/ComponentFlow", + "//submodules/TelegramPresentationData", + "//submodules/Components/MultilineTextComponent", + "//submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/Stories/ForwardInfoPanelComponent/Sources/ForwardInfoPanelComponent.swift b/submodules/TelegramUI/Components/Stories/ForwardInfoPanelComponent/Sources/ForwardInfoPanelComponent.swift new file mode 100644 index 00000000000..922c35d720a --- /dev/null +++ b/submodules/TelegramUI/Components/Stories/ForwardInfoPanelComponent/Sources/ForwardInfoPanelComponent.swift @@ -0,0 +1,191 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import MultilineTextComponent +import MessageInlineBlockBackgroundView + +public final class ForwardInfoPanelComponent: Component { + public let authorName: String + public let text: String + public let isChannel: Bool + public let isVibrant: Bool + public let fillsWidth: Bool + + public init( + authorName: String, + text: String, + isChannel: Bool, + isVibrant: Bool, + fillsWidth: Bool + ) { + self.authorName = authorName + self.text = text + self.isChannel = isChannel + self.isVibrant = isVibrant + self.fillsWidth = fillsWidth + } + + public static func ==(lhs: ForwardInfoPanelComponent, rhs: ForwardInfoPanelComponent) -> Bool { + if lhs.authorName != rhs.authorName { + return false + } + if lhs.text != rhs.text { + return false + } + if lhs.isChannel != rhs.isChannel { + return false + } + if lhs.isVibrant != rhs.isVibrant { + return false + } + if lhs.fillsWidth != rhs.fillsWidth { + return false + } + return true + } + + public final class View: UIView { + public let backgroundView: UIImageView +// private let blurBackgroundView: BlurredBackgroundView + private let blurBackgroundView: UIVisualEffectView + private let blockView: MessageInlineBlockBackgroundView + private var iconView: UIImageView? + private var title = ComponentView() + private var text = ComponentView() + + private var component: ForwardInfoPanelComponent? + private weak var state: EmptyComponentState? + + override init(frame: CGRect) { +// self.blurBackgroundView = BlurredBackgroundView(color: UIColor(rgb: 0x000000, alpha: 0.4)) + + if #available(iOS 13.0, *) { + self.blurBackgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterialDark)) + } else { + self.blurBackgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .dark)) + } + self.blurBackgroundView.clipsToBounds = true + + self.backgroundView = UIImageView() + self.backgroundView.image = generateStretchableFilledCircleImage(radius: 4.0, color: UIColor(white: 1.0, alpha: 0.4)) + + self.blockView = MessageInlineBlockBackgroundView() + + super.init(frame: frame) + + self.addSubview(self.blockView) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: ForwardInfoPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + self.component = component + self.state = state + + var titleOffset: CGFloat = 0.0 + let sideInset: CGFloat = !component.text.isEmpty ? 9.0 : 6.0 + + let iconView: UIImageView + if let current = self.iconView { + iconView = current + } else { + iconView = UIImageView(image: UIImage(bundleImageName: component.isChannel ? "Stories/RepostChannel" : "Stories/RepostUser")?.withRenderingMode(.alwaysTemplate)) + iconView.alpha = 0.55 + iconView.tintColor = .white + self.addSubview(iconView) + } + if let image = iconView.image { + iconView.frame = CGRect(origin: CGPoint(x: sideInset + UIScreenPixel, y: 5.0), size: image.size) + } + titleOffset += 13.0 + + let titleSize = self.title.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: component.authorName, + font: Font.semibold(14.0), + textColor: .white + )), + maximumNumberOfLines: 1 + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - titleOffset - 20.0, height: availableSize.height) + ) + let titleFrame = CGRect(origin: CGPoint(x: sideInset + titleOffset, y: 3.0), size: titleSize) + if let view = self.title.view { + if view.superview == nil { + self.addSubview(view) + } + view.frame = titleFrame + } + + let textSize = self.text.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: component.text, + font: Font.regular(14.0), + textColor: .white + )), + maximumNumberOfLines: 1 + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - 20.0, height: availableSize.height) + ) + let textFrame = CGRect(origin: CGPoint(x: sideInset, y: 20.0), size: textSize) + if let view = self.text.view { + if view.superview == nil { + self.addSubview(view) + } + view.frame = textFrame + } + + let width: CGFloat + let size: CGSize + if component.fillsWidth { + width = availableSize.width + } else { + width = max(titleFrame.maxX, textFrame.maxX) + sideInset + } + + if !component.text.isEmpty { + size = CGSize(width: width, height: 40.0) + let lineColor: UIColor + if !component.isVibrant { + self.blurBackgroundView.frame = CGRect(origin: .zero, size: size) + self.insertSubview(self.blurBackgroundView, at: 0) + + lineColor = UIColor(white: 1.0, alpha: 0.55) + } else { + lineColor = UIColor(white: 1.0, alpha: 0.5) + } + self.blurBackgroundView.layer.cornerRadius = 4.0 + + self.blockView.update(size: size, isTransparent: true, primaryColor: lineColor, secondaryColor: nil, thirdColor: nil, backgroundColor: nil, pattern: nil, animation: .None) + self.blockView.frame = CGRect(origin: .zero, size: size) + } else { + size = CGSize(width: titleFrame.maxX + sideInset, height: 23.0) + + if !component.isVibrant { + self.blurBackgroundView.frame = CGRect(origin: .zero, size: size) + self.insertSubview(self.blurBackgroundView, at: 0) + } + self.blurBackgroundView.layer.cornerRadius = size.height / 2.0 + } + + return size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/BUILD b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/BUILD index b7c19852161..6ac68ffe0f7 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/BUILD +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/BUILD @@ -94,6 +94,7 @@ swift_library( "//submodules/Components/BalancedTextComponent", "//submodules/AnimatedCountLabelNode", "//submodules/StickerResources", + "//submodules/TelegramUI/Components/Stories/ForwardInfoPanelComponent" ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryAuthorInfoComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryAuthorInfoComponent.swift index f60d1f6655e..04ace550cdd 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryAuthorInfoComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryAuthorInfoComponent.swift @@ -7,6 +7,7 @@ import TelegramCore import TelegramStringFormatting import MultilineTextComponent import TelegramPresentationData +import AvatarNode final class StoryAuthorInfoComponent: Component { struct Counters: Equatable { @@ -17,14 +18,16 @@ final class StoryAuthorInfoComponent: Component { let context: AccountContext let strings: PresentationStrings let peer: EnginePeer? + let forwardInfo: EngineStoryItem.ForwardInfo? let timestamp: Int32 let counters: Counters? let isEdited: Bool - init(context: AccountContext, strings: PresentationStrings, peer: EnginePeer?, timestamp: Int32, counters: Counters?, isEdited: Bool) { + init(context: AccountContext, strings: PresentationStrings, peer: EnginePeer?, forwardInfo: EngineStoryItem.ForwardInfo?, timestamp: Int32, counters: Counters?, isEdited: Bool) { self.context = context self.strings = strings self.peer = peer + self.forwardInfo = forwardInfo self.timestamp = timestamp self.counters = counters self.isEdited = isEdited @@ -40,6 +43,9 @@ final class StoryAuthorInfoComponent: Component { if lhs.peer != rhs.peer { return false } + if lhs.forwardInfo != rhs.forwardInfo { + return false + } if lhs.timestamp != rhs.timestamp { return false } @@ -54,6 +60,8 @@ final class StoryAuthorInfoComponent: Component { final class View: UIView { private let title = ComponentView() + private var repostIconView: UIImageView? + private var avatarNode: AvatarNode? private let subtitle = ComponentView() private var counterLabel: ComponentView? @@ -92,11 +100,35 @@ final class StoryAuthorInfoComponent: Component { } let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) - var subtitle = stringForStoryActivityTimestamp(strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, preciseTime: true, relativeTimestamp: component.timestamp, relativeTo: timestamp) - if component.isEdited { - subtitle.append(" • ") - subtitle.append(component.strings.Story_HeaderEdited) + let titleColor = UIColor.white + let subtitleColor = UIColor(white: 1.0, alpha: 0.8) + let subtitle: NSAttributedString + let subtitleTruncationType: CTLineTruncationType + if let forwardInfo = component.forwardInfo { + let authorName: String + switch forwardInfo { + case let .known(peer, _, _): + authorName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + case let .unknown(name, _): + authorName = name + } + let timeString = stringForStoryActivityTimestamp(strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, preciseTime: true, relativeTimestamp: component.timestamp, relativeTo: timestamp, short: true) + let combinedString = NSMutableAttributedString() + combinedString.append(NSAttributedString(string: authorName, font: Font.medium(11.0), textColor: titleColor)) + if timeString.count < 6 { + combinedString.append(NSAttributedString(string: " • \(timeString)", font: Font.regular(11.0), textColor: subtitleColor)) + } + subtitle = combinedString + subtitleTruncationType = .middle + } else { + var subtitleString = stringForStoryActivityTimestamp(strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, preciseTime: true, relativeTimestamp: component.timestamp, relativeTo: timestamp) + if component.isEdited { + subtitleString.append(" • ") + subtitleString.append(component.strings.Story_HeaderEdited) + } + subtitle = NSAttributedString(string: subtitleString, font: Font.regular(11.0), textColor: subtitleColor) + subtitleTruncationType = .end } let titleSize = self.title.update( @@ -112,8 +144,8 @@ final class StoryAuthorInfoComponent: Component { let subtitleSize = self.subtitle.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: subtitle, font: Font.regular(11.0), textColor: UIColor(white: 1.0, alpha: 0.8))), - truncationType: .end, + text: .plain(subtitle), + truncationType: subtitleTruncationType, maximumNumberOfLines: 1 )), environment: {}, @@ -122,7 +154,52 @@ final class StoryAuthorInfoComponent: Component { let contentHeight: CGFloat = titleSize.height + spacing + subtitleSize.height let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: 2.0 + floor((availableSize.height - contentHeight) * 0.5)), size: titleSize) - let subtitleFrame = CGRect(origin: CGPoint(x: leftInset, y: titleFrame.maxY + spacing + UIScreenPixel), size: subtitleSize) + + var subtitleOffset: CGFloat = 0.0 + if let _ = component.forwardInfo { + let iconView: UIImageView + if let current = self.repostIconView { + iconView = current + } else { + iconView = UIImageView(image: UIImage(bundleImageName: "Stories/HeaderRepost")?.withRenderingMode(.alwaysTemplate)) + iconView.tintColor = .white + self.addSubview(iconView) + self.repostIconView = iconView + } + + let iconSize = CGSize(width: 13.0, height: 13.0) + let iconFrame = CGRect(origin: CGPoint(x: leftInset + subtitleOffset - 2.0 + UIScreenPixel, y: titleFrame.minY + contentHeight - iconSize.height + 1.0), size: iconSize) + transition.setFrame(view: iconView, frame: iconFrame) + + subtitleOffset += iconSize.width + 1.0 + } else if let repostIconView = self.repostIconView { + self.repostIconView = nil + repostIconView.removeFromSuperview() + } + if let forwardInfo = component.forwardInfo, case let .known(peer, _, _) = forwardInfo { + let avatarNode: AvatarNode + if let current = self.avatarNode { + avatarNode = current + } else { + avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 8.0)) + self.addSubview(avatarNode.view) + self.avatarNode = avatarNode + } + + let avatarSize = CGSize(width: 16.0, height: 16.0) + let theme = component.context.sharedContext.currentPresentationData.with { $0 }.theme + avatarNode.setPeer(context: component.context, theme: theme, peer: peer, synchronousLoad: true, displayDimensions: avatarSize) + + let avatarFrame = CGRect(origin: CGPoint(x: leftInset + subtitleOffset, y: titleFrame.minY + contentHeight - avatarSize.height + 3.0 - UIScreenPixel), size: avatarSize) + avatarNode.frame = avatarFrame + + subtitleOffset += avatarSize.width + 4.0 + } else if let avatarNode = self.avatarNode { + self.avatarNode = nil + avatarNode.view.removeFromSuperview() + } + + let subtitleFrame = CGRect(origin: CGPoint(x: leftInset + subtitleOffset, y: titleFrame.maxY + spacing + UIScreenPixel), size: subtitleSize) if let titleView = self.title.view { if titleView.superview == nil { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift index 84af7be08e0..3c1abd9c369 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift @@ -41,6 +41,8 @@ public final class StoryContentContextImpl: StoryContentContext { } } + private var currentForwardInfoStories: [StoryId: Promise] = [:] + init(context: AccountContext, peerId: EnginePeer.Id, focusedId initialFocusedId: Int32?, loadIds: @escaping ([StoryKey]) -> Void) { self.context = context self.peerId = peerId @@ -63,9 +65,10 @@ public final class StoryContentContextImpl: StoryContentContext { ), context.engine.data.subscribe(TelegramEngine.EngineData.Item.NotificationSettings.Global()) ) - |> mapToSignal { _, views, globalNotificationSettings -> Signal<(CombinedView, [PeerId: Peer], EngineGlobalNotificationSettings, [MediaId: TelegramMediaFile]), NoError> in - return context.account.postbox.transaction { transaction -> (CombinedView, [PeerId: Peer], EngineGlobalNotificationSettings, [MediaId: TelegramMediaFile]) in + |> mapToSignal { _, views, globalNotificationSettings -> Signal<(CombinedView, [PeerId: Peer], EngineGlobalNotificationSettings, [MediaId: TelegramMediaFile], [Int64: EngineStoryItem.ForwardInfo], [StoryId: EngineStoryItem?]), NoError> in + return context.account.postbox.transaction { transaction -> (CombinedView, [PeerId: Peer], EngineGlobalNotificationSettings, [MediaId: TelegramMediaFile], [Int64: EngineStoryItem.ForwardInfo], [StoryId: EngineStoryItem?]) in var peers: [PeerId: Peer] = [:] + var forwardInfoStories: [StoryId: EngineStoryItem?] = [:] var allEntityFiles: [MediaId: TelegramMediaFile] = [:] if let itemsView = views.views[PostboxViewKey.storyItems(peerId: peerId)] as? StoryItemsView { @@ -78,6 +81,17 @@ public final class StoryContentContextImpl: StoryContentContext { } } } + if let forwardInfo = itemValue.forwardInfo, case let .known(peerId, id, _) = forwardInfo { + if let peer = transaction.getPeer(peerId) { + peers[peer.id] = peer + } + let storyId = StoryId(peerId: peerId, id: id) + if let story = getCachedStory(storyId: storyId, transaction: transaction) { + forwardInfoStories[storyId] = story + } else { + forwardInfoStories.updateValue(nil, forKey: storyId) + } + } for entity in itemValue.entities { if case let .CustomEmoji(_, fileId) = entity.type { let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId) @@ -104,10 +118,19 @@ public final class StoryContentContextImpl: StoryContentContext { } } - return (views, peers, globalNotificationSettings, allEntityFiles) + var pendingForwardsInfo: [Int64: EngineStoryItem.ForwardInfo] = [:] + if let stateView = views.views[PostboxViewKey.storiesState(key: .local)] as? StoryStatesView, let localState = stateView.value?.get(Stories.LocalState.self) { + for item in localState.items { + if let forwardInfo = item.forwardInfo, let peer = transaction.getPeer(forwardInfo.peerId) { + pendingForwardsInfo[item.randomId] = .known(peer: EnginePeer(peer), storyId: forwardInfo.storyId, isModified: forwardInfo.isModified) + } + } + } + + return (views, peers, globalNotificationSettings, allEntityFiles, pendingForwardsInfo, forwardInfoStories) } } - |> deliverOnMainQueue).startStrict(next: { [weak self] views, peers, globalNotificationSettings, allEntityFiles in + |> deliverOnMainQueue).startStrict(next: { [weak self] views, peers, globalNotificationSettings, allEntityFiles, pendingForwardsInfo, forwardInfoStories in guard let self else { return } @@ -129,23 +152,59 @@ public final class StoryContentContextImpl: StoryContentContext { peerPresence = presencesView.presences[peerId] } - if let cachedPeerDataView = views.views[PostboxViewKey.cachedPeerData(peerId: peerId)] as? CachedPeerDataView, let cachedUserData = cachedPeerDataView.cachedPeerData as? CachedUserData { - var isMuted = false - if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings { - isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings, topSearchPeers: []) + for (storyId, story) in forwardInfoStories { + let promise: Promise + var added = false + if let current = self.currentForwardInfoStories[storyId] { + promise = current } else { - isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: nil, topSearchPeers: []) + promise = Promise() + self.currentForwardInfoStories[storyId] = promise + added = true } - additionalPeerData = StoryContentContextState.AdditionalPeerData( - isMuted: isMuted, - areVoiceMessagesAvailable: cachedUserData.voiceMessagesAvailable, - presence: peerPresence.flatMap { EnginePeer.Presence($0) } - ) - } else { + if let story { + promise.set(.single(story)) + } else if added { + promise.set(self.context.engine.messages.getStory(peerId: storyId.peerId, id: storyId.id)) + } + } + + if let cachedPeerDataView = views.views[PostboxViewKey.cachedPeerData(peerId: peerId)] as? CachedPeerDataView { + if let cachedUserData = cachedPeerDataView.cachedPeerData as? CachedUserData { + var isMuted = false + if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings { + isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings, topSearchPeers: []) + } else { + isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: nil, topSearchPeers: []) + } + additionalPeerData = StoryContentContextState.AdditionalPeerData( + isMuted: isMuted, + areVoiceMessagesAvailable: cachedUserData.voiceMessagesAvailable, + presence: peerPresence.flatMap { EnginePeer.Presence($0) }, + canViewStats: false + ) + } else if let cachedChannelData = cachedPeerDataView.cachedPeerData as? CachedChannelData { + additionalPeerData = StoryContentContextState.AdditionalPeerData( + isMuted: true, + areVoiceMessagesAvailable: true, + presence: peerPresence.flatMap { EnginePeer.Presence($0) }, + canViewStats: cachedChannelData.flags.contains(.canViewStats) + ) + } else { + additionalPeerData = StoryContentContextState.AdditionalPeerData( + isMuted: true, + areVoiceMessagesAvailable: true, + presence: peerPresence.flatMap { EnginePeer.Presence($0) }, + canViewStats: false + ) + } + } + else { additionalPeerData = StoryContentContextState.AdditionalPeerData( isMuted: true, areVoiceMessagesAvailable: true, - presence: peerPresence.flatMap { EnginePeer.Presence($0) } + presence: peerPresence.flatMap { EnginePeer.Presence($0) }, + canViewStats: false ) } let state = stateView.value?.get(Stories.PeerState.self) @@ -188,7 +247,8 @@ public final class StoryContentContextImpl: StoryContentContext { isForwardingDisabled: item.isForwardingDisabled, isEdited: item.isEdited, isMy: item.isMy, - myReaction: item.myReaction + myReaction: item.myReaction, + forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) } ) } var totalCount = peerStoryItemsView.items.count @@ -222,7 +282,8 @@ public final class StoryContentContextImpl: StoryContentContext { isForwardingDisabled: false, isEdited: false, isMy: true, - myReaction: nil + myReaction: nil, + forwardInfo: pendingForwardsInfo[item.randomId] )) totalCount += 1 } @@ -338,7 +399,8 @@ public final class StoryContentContextImpl: StoryContentContext { totalCount: totalCount, previousItemId: previousItemId, nextItemId: nextItemId, - allItems: allItems + allItems: allItems, + forwardInfoStories: self.currentForwardInfoStories ) self.isReady = true self.updated.set(.single(Void())) @@ -980,30 +1042,43 @@ public final class SingleStoryContentContextImpl: StoryContentContext { private var requestedStoryKeys = Set() private var requestStoryDisposables = DisposableSet() + private var currentForwardInfoStories: [StoryId: Promise] = [:] + public init( context: AccountContext, storyId: StoryId, + storyItem: EngineStoryItem? = nil, readGlobally: Bool ) { self.context = context self.readGlobally = readGlobally + let item: Signal + if let storyItem { + item = .single(.item(storyItem.asStoryItem())) + } else { + item = context.account.postbox.combinedView(keys: [PostboxViewKey.story(id: storyId)]) + |> map { views -> Stories.StoredItem? in + return (views.views[PostboxViewKey.story(id: storyId)] as? StoryView)?.item?.get(Stories.StoredItem.self) + } + } + self.storyDisposable = (combineLatest(queue: .mainQueue(), context.engine.data.subscribe( TelegramEngine.EngineData.Item.Peer.Peer(id: storyId.peerId), TelegramEngine.EngineData.Item.Peer.Presence(id: storyId.peerId), TelegramEngine.EngineData.Item.Peer.AreVoiceMessagesAvailable(id: storyId.peerId), + TelegramEngine.EngineData.Item.Peer.CanViewStats(id: storyId.peerId), TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: storyId.peerId), TelegramEngine.EngineData.Item.NotificationSettings.Global() ), - context.account.postbox.combinedView(keys: [PostboxViewKey.story(id: storyId)]) |> mapToSignal { views -> Signal<(Stories.StoredItem?, [PeerId: Peer], [MediaId: TelegramMediaFile]), NoError> in - let item = (views.views[PostboxViewKey.story(id: storyId)] as? StoryView)?.item?.get(Stories.StoredItem.self) - - return context.account.postbox.transaction { transaction -> (Stories.StoredItem?, [PeerId: Peer], [MediaId: TelegramMediaFile]) in + item |> mapToSignal { item -> Signal<(Stories.StoredItem?, [PeerId: Peer], [MediaId: TelegramMediaFile], [StoryId: EngineStoryItem?]), NoError> in + return context.account.postbox.transaction { transaction -> (Stories.StoredItem?, [PeerId: Peer], [MediaId: TelegramMediaFile], [StoryId: EngineStoryItem?]) in guard let item else { - return (nil, [:], [:]) + return (nil, [:], [:], [:]) } var peers: [PeerId: Peer] = [:] + var stories: [StoryId: EngineStoryItem?] = [:] var allEntityFiles: [MediaId: TelegramMediaFile] = [:] if case let .item(item) = item { if let views = item.views { @@ -1013,6 +1088,17 @@ public final class SingleStoryContentContextImpl: StoryContentContext { } } } + if let forwardInfo = item.forwardInfo, case let .known(peerId, id, _) = forwardInfo { + if let peer = transaction.getPeer(peerId) { + peers[peer.id] = peer + } + let storyId = StoryId(peerId: peerId, id: id) + if let story = getCachedStory(storyId: storyId, transaction: transaction) { + stories[storyId] = story + } else { + stories.updateValue(nil, forKey: storyId) + } + } for entity in item.entities { if case let .CustomEmoji(_, fileId) = entity.type { let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId) @@ -1036,7 +1122,7 @@ public final class SingleStoryContentContextImpl: StoryContentContext { } } } - return (item, peers, allEntityFiles) + return (item, peers, allEntityFiles, stories) } } ) @@ -1045,8 +1131,8 @@ public final class SingleStoryContentContextImpl: StoryContentContext { return } - let (peer, presence, areVoiceMessagesAvailable, notificationSettings, globalNotificationSettings) = data - let (item, peers, allEntityFiles) = itemAndPeers + let (peer, presence, areVoiceMessagesAvailable, canViewStats, notificationSettings, globalNotificationSettings) = data + let (item, peers, allEntityFiles, forwardInfoStories) = itemAndPeers guard let peer else { return @@ -1057,9 +1143,27 @@ public final class SingleStoryContentContextImpl: StoryContentContext { let additionalPeerData = StoryContentContextState.AdditionalPeerData( isMuted: isMuted, areVoiceMessagesAvailable: areVoiceMessagesAvailable, - presence: presence + presence: presence, + canViewStats: canViewStats ) + for (storyId, story) in forwardInfoStories { + let promise: Promise + var added = false + if let current = self.currentForwardInfoStories[storyId] { + promise = current + } else { + promise = Promise() + self.currentForwardInfoStories[storyId] = promise + added = true + } + if let story { + promise.set(.single(story)) + } else if added { + promise.set(self.context.engine.messages.getStory(peerId: storyId.peerId, id: storyId.id)) + } + } + if item == nil { let storyKey = StoryKey(peerId: storyId.peerId, id: storyId.id) if !self.requestedStoryKeys.contains(storyKey) { @@ -1101,7 +1205,8 @@ public final class SingleStoryContentContextImpl: StoryContentContext { isForwardingDisabled: itemValue.isForwardingDisabled, isEdited: itemValue.isEdited, isMy: itemValue.isMy, - myReaction: itemValue.myReaction + myReaction: itemValue.myReaction, + forwardInfo: itemValue.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) } ) let mainItem = StoryContentItem( @@ -1119,7 +1224,8 @@ public final class SingleStoryContentContextImpl: StoryContentContext { totalCount: 1, previousItemId: nil, nextItemId: nil, - allItems: [mainItem] + allItems: [mainItem], + forwardInfoStories: self.currentForwardInfoStories ), previousSlice: nil, nextSlice: nil @@ -1201,6 +1307,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { TelegramEngine.EngineData.Item.Peer.Peer(id: peerId), TelegramEngine.EngineData.Item.Peer.Presence(id: peerId), TelegramEngine.EngineData.Item.Peer.AreVoiceMessagesAvailable(id: peerId), + TelegramEngine.EngineData.Item.Peer.CanViewStats(id: peerId), TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peerId), TelegramEngine.EngineData.Item.NotificationSettings.Global() ), @@ -1212,7 +1319,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { return } - let (peer, presence, areVoiceMessagesAvailable, notificationSettings, globalNotificationSettings) = data + let (peer, presence, areVoiceMessagesAvailable, canViewStats, notificationSettings, globalNotificationSettings) = data guard let peer else { return @@ -1223,7 +1330,8 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { let additionalPeerData = StoryContentContextState.AdditionalPeerData( isMuted: isMuted, areVoiceMessagesAvailable: areVoiceMessagesAvailable, - presence: presence + presence: presence, + canViewStats: canViewStats ) self.listState = state @@ -1323,7 +1431,8 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { totalCount: state.totalCount, previousItemId: focusedIndex == 0 ? nil : state.items[focusedIndex - 1].id, nextItemId: (focusedIndex == state.items.count - 1) ? nil : state.items[focusedIndex + 1].id, - allItems: allItems + allItems: allItems, + forwardInfoStories: [:] ), previousSlice: nil, nextSlice: nil @@ -1880,3 +1989,44 @@ func extractItemEntityFiles(item: EngineStoryItem, allEntityFiles: [MediaId: Tel } return result } + +private func getCachedStory(storyId: StoryId, transaction: Transaction) -> EngineStoryItem? { + if let storyItem = transaction.getStory(id: storyId)?.get(Stories.StoredItem.self), case let .item(item) = storyItem, let media = item.media { + return EngineStoryItem( + id: item.id, + timestamp: item.timestamp, + expirationTimestamp: item.expirationTimestamp, + media: EngineMedia(media), + mediaAreas: item.mediaAreas, + text: item.text, + entities: item.entities, + views: item.views.flatMap { views in + return EngineStoryItem.Views( + seenCount: views.seenCount, + reactedCount: views.reactedCount, + forwardCount: views.forwardCount, + seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in + return transaction.getPeer(id).flatMap(EnginePeer.init) + }, + reactions: views.reactions, + hasList: views.hasList + ) + }, + privacy: item.privacy.flatMap(EngineStoryPrivacy.init), + isPinned: item.isPinned, + isExpired: item.isExpired, + isPublic: item.isPublic, + isPending: false, + isCloseFriends: item.isCloseFriends, + isContacts: item.isContacts, + isSelectedContacts: item.isSelectedContacts, + isForwardingDisabled: item.isForwardingDisabled, + isEdited: item.isEdited, + isMy: item.isMy, + myReaction: item.myReaction, + forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) } + ) + } else { + return nil + } +} diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift index 96c0b8a7a06..694e57159d8 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift @@ -502,7 +502,7 @@ private final class StoryContainerScreenComponent: Component { } } longPressRecognizer.updatePanMove = { [weak self] initialLocation, translation in - guard let self else { + guard let self, self.itemSetPanState?.didBegin == false else { return } guard let stateValue = self.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View else { @@ -538,11 +538,7 @@ private final class StoryContainerScreenComponent: Component { let fraction = translation.x / (self.bounds.width / 2.0) timestamp = initialSeekTimestamp + duration * fraction } - if translation.y < 64.0 { - visibleItemView.seekTo(max(0.0, min(duration, timestamp)), apply: apply) - } else { - visibleItemView.seekTo(initialSeekTimestamp, apply: apply) - } + visibleItemView.seekTo(max(0.0, min(duration, timestamp)), apply: apply) } longPressRecognizer.updatePanEnded = { [weak self] in guard let self else { @@ -1124,15 +1120,16 @@ private final class StoryContainerScreenComponent: Component { } } + private var previousBackNavigationTime: Double? private func navigate(direction: StoryItemSetContainerComponent.NavigationDirection) { - guard let component = self.component, let environment = self.environment else { + guard let component = self.component, let environment = self.environment, let controller = environment.controller() as? StoryContainerScreen else { return } if let stateValue = self.stateValue, let slice = stateValue.slice { if case .next = direction, slice.nextItemId == nil, (slice.item.position == nil || slice.item.position == slice.totalCount - 1) { if stateValue.nextSlice == nil { - environment.controller()?.dismiss() + controller.dismiss() } else { self.beginHorizontalPan(translation: CGPoint()) self.updateHorizontalPan(translation: CGPoint()) @@ -1142,7 +1139,17 @@ private final class StoryContainerScreenComponent: Component { if stateValue.previousSlice == nil { if let itemSetView = self.visibleItemSetViews[slice.peer.id] { if let componentView = itemSetView.view.view as? StoryItemSetContainerComponent.View { - componentView.rewindCurrentItem() + if let customBackAction = controller.customBackAction { + let currentTime = CACurrentMediaTime() + if let previousBackNavigationTime = self.previousBackNavigationTime, currentTime - previousBackNavigationTime < 1.0 { + customBackAction() + } else { + self.previousBackNavigationTime = CACurrentMediaTime() + componentView.rewindCurrentItem() + } + } else { + componentView.rewindCurrentItem() + } } } } else { @@ -1943,6 +1950,8 @@ public class StoryContainerScreen: ViewControllerComponentContainer { return self.focusedItemPromise.get() } + public var customBackAction: (() -> Void)? + public init( context: AccountContext, content: StoryContentContext, diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift index 7956cd8b7d3..0202af58cf6 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift @@ -140,15 +140,18 @@ public final class StoryContentContextState { public let isMuted: Bool public let areVoiceMessagesAvailable: Bool public let presence: EnginePeer.Presence? + public let canViewStats: Bool public init( isMuted: Bool, areVoiceMessagesAvailable: Bool, - presence: EnginePeer.Presence? + presence: EnginePeer.Presence?, + canViewStats: Bool ) { self.isMuted = isMuted self.areVoiceMessagesAvailable = areVoiceMessagesAvailable self.presence = presence + self.canViewStats = canViewStats } public static func == (lhs: StoryContentContextState.AdditionalPeerData, rhs: StoryContentContextState.AdditionalPeerData) -> Bool { @@ -161,6 +164,9 @@ public final class StoryContentContextState { if lhs.presence != rhs.presence { return false } + if lhs.canViewStats != rhs.canViewStats { + return false + } return true } } @@ -173,6 +179,7 @@ public final class StoryContentContextState { public let previousItemId: Int32? public let nextItemId: Int32? public let allItems: [StoryContentItem] + public let forwardInfoStories: [StoryId: Promise] public init( peer: EnginePeer, @@ -181,7 +188,8 @@ public final class StoryContentContextState { totalCount: Int, previousItemId: Int32?, nextItemId: Int32?, - allItems: [StoryContentItem] + allItems: [StoryContentItem], + forwardInfoStories: [StoryId: Promise] ) { self.peer = peer self.additionalPeerData = additionalPeerData @@ -190,6 +198,7 @@ public final class StoryContentContextState { self.previousItemId = previousItemId self.nextItemId = nextItemId self.allItems = allItems + self.forwardInfoStories = forwardInfoStories } public static func ==(lhs: FocusedSlice, rhs: FocusedSlice) -> Bool { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentCaptionComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentCaptionComponent.swift index 6f7cc5ec629..5ebe263fe95 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentCaptionComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentCaptionComponent.swift @@ -4,6 +4,7 @@ import Display import ComponentFlow import MultilineTextComponent import AccountContext +import Postbox import TelegramCore import TextNodeWithEntities import TextFormat @@ -11,6 +12,9 @@ import InvisibleInkDustNode import UrlEscaping import TelegramPresentationData import TextSelectionNode +import SwiftSignalKit +import ForwardInfoPanelComponent +import PlainButtonComponent final class StoryContentCaptionComponent: Component { enum Action { @@ -55,12 +59,16 @@ final class StoryContentCaptionComponent: Component { let strings: PresentationStrings let theme: PresentationTheme let text: String + let author: EnginePeer + let forwardInfo: EngineStoryItem.ForwardInfo? + let forwardInfoStory: Signal? let entities: [MessageTextEntity] let entityFiles: [EngineMedia.Id: TelegramMediaFile] let action: (Action) -> Void let longTapAction: (Action) -> Void let textSelectionAction: (NSAttributedString, TextSelectionAction) -> Void let controller: () -> ViewController? + let openStory: (EnginePeer, EngineStoryItem) -> Void init( externalState: ExternalState, @@ -68,17 +76,24 @@ final class StoryContentCaptionComponent: Component { strings: PresentationStrings, theme: PresentationTheme, text: String, + author: EnginePeer, + forwardInfo: EngineStoryItem.ForwardInfo?, + forwardInfoStory: Signal?, entities: [MessageTextEntity], entityFiles: [EngineMedia.Id: TelegramMediaFile], action: @escaping (Action) -> Void, longTapAction: @escaping (Action) -> Void, textSelectionAction: @escaping (NSAttributedString, TextSelectionAction) -> Void, - controller: @escaping () -> ViewController? + controller: @escaping () -> ViewController?, + openStory: @escaping (EnginePeer, EngineStoryItem) -> Void ) { self.externalState = externalState self.context = context self.strings = strings self.theme = theme + self.author = author + self.forwardInfo = forwardInfo + self.forwardInfoStory = forwardInfoStory self.text = text self.entities = entities self.entityFiles = entityFiles @@ -86,6 +101,7 @@ final class StoryContentCaptionComponent: Component { self.longTapAction = longTapAction self.textSelectionAction = textSelectionAction self.controller = controller + self.openStory = openStory } static func ==(lhs: StoryContentCaptionComponent, rhs: StoryContentCaptionComponent) -> Bool { @@ -101,6 +117,12 @@ final class StoryContentCaptionComponent: Component { if lhs.theme !== rhs.theme { return false } + if lhs.author != rhs.author { + return false + } + if lhs.forwardInfo != rhs.forwardInfo { + return false + } if lhs.text != rhs.text { return false } @@ -161,10 +183,15 @@ final class StoryContentCaptionComponent: Component { private let scrollBottomFullMaskView: UIView private let scrollTopMaskView: UIImageView + private var forwardInfoPanel: ComponentView? + private var forwardInfoDisposable: Disposable? + private var forwardInfoStory: EngineStoryItem? + private let shadowGradientView: UIImageView private var component: StoryContentCaptionComponent? private weak var state: EmptyComponentState? + private var isUpdating: Bool = false private var itemLayout: ItemLayout? @@ -173,6 +200,9 @@ final class StoryContentCaptionComponent: Component { private var isExpanded: Bool = false + private var codeHighlight: CachedMessageSyntaxHighlight? + private var codeHighlightState: (specs: [CachedMessageSyntaxHighlight.Spec], disposable: Disposable)? + private static let shadowImage: UIImage? = { UIImage(named: "Stories/PanelGradient") }() @@ -245,6 +275,11 @@ final class StoryContentCaptionComponent: Component { fatalError("init(coder:) has not been implemented") } + deinit { + self.codeHighlightState?.disposable.dispose() + self.forwardInfoDisposable?.dispose() + } + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { if !self.bounds.contains(point) { return nil @@ -259,6 +294,13 @@ final class StoryContentCaptionComponent: Component { } } + if let forwardView = self.forwardInfoPanel?.view { + let forwardLocalPoint = self.convert(point, to: forwardView) + if let result = forwardView.hitTest(forwardLocalPoint, with: nil) { + return result + } + } + return nil } @@ -518,6 +560,11 @@ final class StoryContentCaptionComponent: Component { } func update(component: StoryContentCaptionComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } + self.ignoreExternalState = true self.component = component @@ -527,11 +574,47 @@ final class StoryContentCaptionComponent: Component { let verticalInset: CGFloat = 7.0 let textContainerSize = CGSize(width: availableSize.width - sideInset * 2.0, height: availableSize.height - verticalInset * 2.0) + var baseQuoteSecondaryTintColor: UIColor? + var baseQuoteTertiaryTintColor: UIColor? + if let nameColor = component.author.nameColor { + let resolvedColor = component.context.peerNameColors.get(nameColor) + if resolvedColor.secondary != nil { + baseQuoteSecondaryTintColor = .clear + } + if resolvedColor.tertiary != nil { + baseQuoteTertiaryTintColor = .clear + } + } + + let codeSpec = extractMessageSyntaxHighlightSpecs(text: component.text, entities: component.entities) + if self.codeHighlightState?.specs != codeSpec { + let disposable = MetaDisposable() + self.codeHighlightState = (codeSpec, disposable) + disposable.set((asyncStanaloneSyntaxHighlight(current: self.codeHighlight, specs: codeSpec) + |> deliverOnMainQueue).start(next: { [weak self] result in + guard let self else { + return + } + if self.codeHighlight != result { + self.codeHighlight = result + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } + } + })) + } + let attributedText = stringWithAppliedEntities( component.text, entities: component.entities, baseColor: .white, linkColor: .white, + baseQuoteTintColor: .white, + baseQuoteSecondaryTintColor: baseQuoteSecondaryTintColor, + baseQuoteTertiaryTintColor: baseQuoteTertiaryTintColor, + codeBlockTitleColor: .white, + codeBlockAccentColor: .white, + codeBlockBackgroundColor: UIColor(white: 1.0, alpha: 0.2), baseFont: Font.regular(16.0), linkFont: Font.regular(16.0), boldFont: Font.semibold(16.0), @@ -541,7 +624,8 @@ final class StoryContentCaptionComponent: Component { blockQuoteFont: Font.monospace(16.0), message: nil, entityFiles: component.entityFiles, - adjustQuoteFontSize: true + adjustQuoteFontSize: true, + cachedMessageSyntaxHighlight: self.codeHighlight ) let truncationToken = NSMutableAttributedString() @@ -586,6 +670,94 @@ final class StoryContentCaptionComponent: Component { let textOverflowHeight: CGFloat = expandedTextLayout.0.size.height - visibleTextHeight let scrollContentSize = CGSize(width: availableSize.width, height: availableSize.height + textOverflowHeight) + if let forwardInfo = component.forwardInfo { + let authorName: String + let isChannel: Bool + let text: String? + + switch forwardInfo { + case let .known(peer, _, _): + authorName = peer.displayTitle(strings: component.strings, displayOrder: .firstLast) + isChannel = peer.id.isGroupOrChannel + + if let story = self.forwardInfoStory { + text = story.text + } else if self.forwardInfoDisposable == nil, let forwardInfoStory = component.forwardInfoStory { + self.forwardInfoDisposable = (forwardInfoStory + |> deliverOnMainQueue).start(next: { story in + if let story { + self.forwardInfoStory = story + if !self.isUpdating { + self.state?.updated(transition: .easeInOut(duration: 0.2)) + } + } + }) + text = nil + } else { + text = nil + } + case let .unknown(name, _): + authorName = name + isChannel = false + text = "" + } + + if let text { + let forwardInfoPanel: ComponentView + if let current = self.forwardInfoPanel { + forwardInfoPanel = current + } else { + forwardInfoPanel = ComponentView() + self.forwardInfoPanel = forwardInfoPanel + } + + let forwardInfoPanelSize = forwardInfoPanel.update( + transition: .immediate, + component: AnyComponent( + PlainButtonComponent( + content: AnyComponent( + ForwardInfoPanelComponent( + authorName: authorName, + text: text, + isChannel: isChannel, + isVibrant: false, + fillsWidth: false + ) + ), + effectAlignment: .center, + minSize: nil, + action: { [weak self] in + if let self, case let .known(peer, _, _) = forwardInfo, let story = self.forwardInfoStory { + self.component?.openStory(peer, story) + } else if let controller = self?.component?.controller() as? StoryContainerScreen { + let tooltipController = TooltipController(content: .text(component.strings.Story_ForwardAuthorHiddenTooltip), baseFontSize: 17.0, isBlurred: true, dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true) + controller.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceNodeAndRect: { [weak self, weak controller] in + if let self, let controller, let forwardInfoPanel = self.forwardInfoPanel?.view { + return (controller.node, forwardInfoPanel.convert(forwardInfoPanel.bounds, to: controller.view)) + } + return nil + })) + } + } + ) + ), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: availableSize.height) + ) + let forwardInfoPanelFrame = CGRect(origin: CGPoint(x: sideInset, y: availableSize.height - visibleTextHeight - verticalInset - forwardInfoPanelSize.height - 10.0), size: forwardInfoPanelSize) + if let view = forwardInfoPanel.view { + if view.superview == nil { + self.scrollView.addSubview(view) + transition.animateAlpha(view: view, from: 0.0, to: 1.0) + } + view.frame = forwardInfoPanelFrame + } + } + } else if let forwardInfoPanel = self.forwardInfoPanel { + self.forwardInfoPanel = nil + forwardInfoPanel.view?.removeFromSuperview() + } + do { let collapsedTextNode = collapsedTextLayout.1(TextNodeWithEntities.Arguments( context: component.context, diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift index 33a5a51fc04..979bd22dc15 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift @@ -83,6 +83,7 @@ final class StoryItemContentComponent: Component { private let overlaysView: StoryItemOverlaysView private var videoNode: UniversalVideoNode? private var loadingEffectView: StoryItemLoadingEffectView? + private var loadingEffectAppearanceTimer: SwiftSignalKit.Timer? private var mediaAreasEffectView: StoryItemLoadingEffectView? @@ -826,11 +827,29 @@ final class StoryItemContentComponent: Component { loadingEffectView = current } else { loadingEffectView = StoryItemLoadingEffectView(effectAlpha: 0.1, borderAlpha: 0.2, duration: 1.0, hasCustomBorder: false, playOnce: false) + loadingEffectView.alpha = 0.0 self.loadingEffectView = loadingEffectView self.addSubview(loadingEffectView) + + if self.loadingEffectAppearanceTimer == nil { + let timer = SwiftSignalKit.Timer(timeout: 0.2, repeat: false, completion: { [weak self] in + guard let self else { + return + } + if let loadingEffectView = self.loadingEffectView { + loadingEffectView.alpha = 1.0 + loadingEffectView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18) + } + self.loadingEffectAppearanceTimer = nil + }, queue: Queue.mainQueue()) + timer.start() + self.loadingEffectAppearanceTimer = timer + } } loadingEffectView.update(size: availableSize, transition: transition) } else if let loadingEffectView = self.loadingEffectView { + self.loadingEffectAppearanceTimer?.invalidate() + self.loadingEffectAppearanceTimer = nil self.loadingEffectView = nil loadingEffectView.layer.animateAlpha(from: loadingEffectView.alpha, to: 0.0, duration: 0.18, removeOnCompletion: false, completion: { [weak loadingEffectView] _ in loadingEffectView?.removeFromSuperview() diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index 5598bab0dbd..9bcf6235c7a 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -1220,10 +1220,13 @@ public final class StoryItemSetContainerComponent: Component { self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring))) } } else { + if let visibleItemView = self.visibleItems[component.slice.item.storyItem.id]?.view.view as? StoryItemContentComponent.View { + visibleItemView.seekEnded() + } if translation.y > 200.0 || (translation.y > 5.0 && velocity.y > 200.0) { self.state?.updated(transition: Transition(animation: .curve(duration: 0.3, curve: .spring))) self.component?.controller()?.dismiss() - } else if translation.y < -200.0 || (translation.y < -100.0 && velocity.y < -100.0) { + } else if translation.y < -200.0 || (translation.y < -100.0 && velocity.y < -100.0) { var displayViewLists = false if component.slice.peer.id == component.context.account.peerId { displayViewLists = true @@ -1647,15 +1650,22 @@ public final class StoryItemSetContainerComponent: Component { } var isChannel = false + var canShare = true var displayFooter = false - if case .channel = component.slice.peer { + if case let .channel(channel) = component.slice.peer { displayFooter = true isChannel = true + if channel.addressName == nil { + canShare = false + } } else if component.slice.peer.id == component.context.account.peerId { displayFooter = true } else if component.slice.item.storyItem.isPending { displayFooter = true } + if component.slice.item.storyItem.isForwardingDisabled { + canShare = false + } if displayFooter { let contentViewsShadowView: UIImageView @@ -1719,6 +1729,7 @@ public final class StoryItemSetContainerComponent: Component { return StoryFooterPanelComponent.MyReaction(reaction: value, file: centerAnimation, animationFileId: animationFileId) }, isChannel: isChannel, + canShare: canShare, externalViews: nil, expandFraction: footerExpandFraction, expandViewStats: { [weak self] in @@ -1796,6 +1807,19 @@ public final class StoryItemSetContainerComponent: Component { return } self.sendMessageContext.performShareAction(view: self) + }, + repostAction: { [weak self] in + guard let self else { + return + } + self.openStoryEditing(repost: true) + }, + cancelUploadAction: { [weak self] in + guard let self, let component = self.component, let controller = self.component?.controller() as? StoryContainerScreen else { + return + } + component.context.engine.messages.cancelStoryUpload(stableId: component.slice.item.storyItem.id) + controller.dismissWithoutTransitionOut() } )), environment: {}, @@ -2839,7 +2863,7 @@ public final class StoryItemSetContainerComponent: Component { } self.sendMessageContext.performSendStickerAction(view: self, fileReference: .standalone(media: sticker)) }, - setMediaRecordingActive: { [weak self] isActive, isVideo, sendAction in + setMediaRecordingActive: { [weak self] isActive, isVideo, sendAction, _ in guard let self else { return } @@ -2920,7 +2944,7 @@ public final class StoryItemSetContainerComponent: Component { } }, timeoutAction: nil, - forwardAction: component.slice.item.storyItem.isPublic ? { [weak self] in + forwardAction: component.slice.item.storyItem.isPublic && !component.slice.item.storyItem.isForwardingDisabled ? { [weak self] in guard let self else { return } @@ -2939,7 +2963,7 @@ public final class StoryItemSetContainerComponent: Component { let rect = view.convert(view.bounds, to: nil) let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } let text = presentationData.strings.Conversation_VoiceMessagesRestricted(component.slice.peer.compactDisplayTitle).string - let controller = TooltipController(content: .text(text), baseFontSize: presentationData.listsFontSize.baseDisplaySize, padding: 2.0) + let controller = TooltipController(content: .text(text), baseFontSize: presentationData.listsFontSize.baseDisplaySize, isBlurred: true, padding: 2.0) controller.dismissed = { [weak self] _ in if let self { self.voiceMessagesRestrictedTooltipController = nil @@ -2980,6 +3004,7 @@ public final class StoryItemSetContainerComponent: Component { audioRecorder: self.sendMessageContext.audioRecorderValue, videoRecordingStatus: !self.sendMessageContext.hasRecordedVideoPreview ? self.sendMessageContext.videoRecorderValue?.audioStatus : nil, isRecordingLocked: self.sendMessageContext.isMediaRecordingLocked, + hasRecordedVideo: false, recordedAudioPreview: self.sendMessageContext.recordedAudioPreview, hasRecordedVideoPreview: self.sendMessageContext.hasRecordedVideoPreview, wasRecordingDismissed: self.sendMessageContext.wasRecordingDismissed, @@ -2992,6 +3017,7 @@ public final class StoryItemSetContainerComponent: Component { customInputView: nil, forceIsEditing: self.sendMessageContext.currentInputMode == .media, disabledPlaceholder: disabledPlaceholder, + header: nil, isChannel: isChannel, storyItem: component.slice.item.storyItem, chatLocation: nil @@ -3936,6 +3962,7 @@ public final class StoryItemSetContainerComponent: Component { context: component.context, strings: component.strings, peer: component.slice.peer, + forwardInfo: component.slice.item.storyItem.forwardInfo, timestamp: component.slice.item.storyItem.timestamp, counters: counters, isEdited: component.slice.item.storyItem.isEdited @@ -3963,14 +3990,22 @@ public final class StoryItemSetContainerComponent: Component { guard let self, let component = self.component else { return } - if component.slice.peer.id == component.context.account.peerId { - self.navigateToMyStories() + if let forwardInfo = component.slice.item.storyItem.forwardInfo, case let .known(peer, _, _) = forwardInfo { + if peer.id == component.context.account.peerId { + self.navigateToMyStories() + } else { + self.navigateToPeer(peer: peer, chat: false) + } } else { - self.navigateToPeer(peer: component.slice.peer, chat: false) + if component.slice.peer.id == component.context.account.peerId { + self.navigateToMyStories() + } else { + self.navigateToPeer(peer: component.slice.peer, chat: false) + } } })), environment: {}, - containerSize: CGSize(width: headerRightOffset - 10.0, height: 44.0) + containerSize: CGSize(width: headerRightOffset - 34.0, height: 44.0) ) if let view = currentCenterInfoItem.view.view { var animateIn = false @@ -4061,7 +4096,7 @@ public final class StoryItemSetContainerComponent: Component { } } - if !isUnsupported, !component.slice.item.storyItem.text.isEmpty { + if !isUnsupported, !component.slice.item.storyItem.text.isEmpty || component.slice.item.storyItem.forwardInfo != nil { var captionItemTransition = transition let captionItem: CaptionItem if let current = self.captionItem { @@ -4080,7 +4115,10 @@ public final class StoryItemSetContainerComponent: Component { enableEntities = false } } - + var forwardInfoStory: Signal? = nil + if let forwardInfo = component.slice.item.storyItem.forwardInfo, case let .known(peer, id, _) = forwardInfo { + forwardInfoStory = component.slice.forwardInfoStories[StoryId(peerId: peer.id, id: id)]?.get() + } let captionSize = captionItem.view.update( transition: captionItemTransition, component: AnyComponent(StoryContentCaptionComponent( @@ -4089,6 +4127,9 @@ public final class StoryItemSetContainerComponent: Component { strings: component.strings, theme: component.theme, text: component.slice.item.storyItem.text, + author: component.slice.peer, + forwardInfo: component.slice.item.storyItem.forwardInfo, + forwardInfoStory: forwardInfoStory, entities: enableEntities ? component.slice.item.storyItem.entities : [], entityFiles: component.slice.item.entityFiles, action: { [weak self] action in @@ -4178,6 +4219,51 @@ public final class StoryItemSetContainerComponent: Component { return nil } return component.controller() + }, + openStory: { [weak self] peer, story in + guard let self, let component = self.component else { + return + } + let context = component.context + let peerId = component.slice.peer.id + let currentResult: ResolvedUrl = .story(peerId: peerId, id: component.slice.item.storyItem.id) + + self.sendMessageContext.openResolved(view: self, result: .story(peerId: peer.id, id: story.id), completion: { [weak self] in + guard let self, let controller = self.component?.controller() as? StoryContainerScreen else { + return + } + if let nextController = controller.navigationController?.viewControllers.last as? StoryContainerScreen { + nextController.customBackAction = { [weak nextController] in + context.sharedContext.openResolvedUrl( + currentResult, + context: context, + urlContext: .generic, + navigationController: nextController?.navigationController as? NavigationController, + forceExternal: false, + openPeer: { _, _ in + }, + sendFile: nil, + sendSticker: nil, + requestMessageActionUrlAuth: nil, + joinVoiceChat: nil, + present: { _, _ in + }, + dismissInput: { + }, + contentContext: nil, + progress: nil, + completion: { [weak nextController] in + Queue.mainQueue().after(0.5) { + nextController?.dismissWithoutTransitionOut() + } + } + ) + } + } + Queue.mainQueue().after(0.5) { + controller.dismissWithoutTransitionOut() + } + }) } )), environment: {}, @@ -4252,6 +4338,7 @@ public final class StoryItemSetContainerComponent: Component { items: reactionItems.map(ReactionContextItem.reaction), selectedItems: component.slice.item.storyItem.myReaction.flatMap { Set([$0]) } ?? Set(), title: self.displayLikeReactions ? nil : component.strings.Story_SendReactionAsMessage, + alwaysAllowPremiumReactions: false, getEmojiContent: { [weak self] animationCache, animationRenderer in guard let self, let component = self.component else { preconditionFailure() @@ -4266,7 +4353,7 @@ public final class StoryItemSetContainerComponent: Component { animationCache: animationCache, animationRenderer: animationRenderer, isStandalone: false, - subject: .reaction, + subject: .reaction(onlyTop: false), hasTrending: false, topReactionItems: mappedReactionItems, areUnicodeEmojiEnabled: false, @@ -5074,7 +5161,7 @@ public final class StoryItemSetContainerComponent: Component { StoryContainerScreen.openPeerStories(context: component.context, peerId: peer.id, parentController: controller, avatarNode: avatarNode) } - private func openStoryEditing() { + func openStoryEditing(repost: Bool = false) { guard let component = self.component, let peerReference = PeerReference(component.slice.peer._asPeer()) else { return } @@ -5095,7 +5182,7 @@ public final class StoryItemSetContainerComponent: Component { let subject: Signal subject = getStorySource(engine: component.context.engine, peerId: component.context.account.peerId, id: Int64(item.id)) |> mapToSignal { source in - if let source { + if !repost, let source { return .single(.draft(source, Int64(item.id))) } else { let media = item.media._asMedia() @@ -5128,148 +5215,223 @@ public final class StoryItemSetContainerComponent: Component { } } + let initialCaption: NSAttributedString? + let initialPrivacy: EngineStoryPrivacy? + let initialMediaAreas: [MediaArea] + if repost { + initialCaption = nil + initialPrivacy = nil + initialMediaAreas = [] + } else { + initialCaption = chatInputStateStringWithAppliedEntities(item.text, entities: item.entities) + initialPrivacy = item.privacy + initialMediaAreas = item.mediaAreas + } + + let externalState = MediaEditorTransitionOutExternalState( + storyTarget: nil, + isPeerArchived: false, + transitionOut: nil + ) + let updateDisposable = MetaDisposable() var updateProgressImpl: ((Float) -> Void)? let controller = MediaEditorScreen( context: context, subject: subject, - isEditing: true, - initialCaption: chatInputStateStringWithAppliedEntities(item.text, entities: item.entities), - initialPrivacy: item.privacy, - initialMediaAreas: item.mediaAreas, + isEditing: !repost, + forwardSource: repost ? (component.slice.peer, item) : nil, + initialCaption: initialCaption, + initialPrivacy: initialPrivacy, + initialMediaAreas: initialMediaAreas, initialVideoPosition: videoPlaybackPosition, transitionIn: nil, - transitionOut: { _, _ in return nil }, - completion: { [weak self] _, mediaResult, mediaAreas, caption, privacy, stickers, commit in + transitionOut: { finished, isNew in + if repost && finished { + if let transitionOut = externalState.transitionOut?(externalState.storyTarget, externalState.isPeerArchived), let destinationView = transitionOut.destinationView { + return MediaEditorScreen.TransitionOut( + destinationView: destinationView, + destinationRect: transitionOut.destinationRect, + destinationCornerRadius: transitionOut.destinationCornerRadius + ) + } else { + return nil + } + } else { + return nil + } + }, + completion: { [weak self] result, commit in guard let self else { return } - let entities = generateChatInputTextEntities(caption) - var updatedText: String? - var updatedEntities: [MessageTextEntity]? - if caption.string != item.text || entities != item.entities { - updatedText = caption.string - updatedEntities = entities - } + + let entities = generateChatInputTextEntities(result.caption) - if let mediaResult { - switch mediaResult { - case let .image(image, dimensions): - updateProgressImpl?(0.0) - - let tempFile = TempBox.shared.tempFile(fileName: "file") - defer { - TempBox.shared.dispose(tempFile) + if repost { + let target: Stories.PendingTarget + let targetPeerId: EnginePeer.Id + if let sendAsPeerId = result.options.sendAsPeerId { + target = .peer(sendAsPeerId) + targetPeerId = sendAsPeerId + } else { + target = .myStories + targetPeerId = context.account.peerId + } + externalState.storyTarget = target + + self.component?.controller()?.dismiss(animated: false) + + let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: targetPeerId)) + |> deliverOnMainQueue).startStandalone(next: { peer in + guard let peer else { + return } - if let imageData = compressImageToJPEG(image, quality: 0.7, tempFilePath: tempFile.path) { - updateDisposable.set((context.engine.messages.editStory(peerId: peerId, id: id, media: .image(dimensions: dimensions, data: imageData, stickers: stickers), mediaAreas: mediaAreas, text: updatedText, entities: updatedEntities, privacy: nil) - |> deliverOnMainQueue).startStrict(next: { [weak self] result in - guard let self else { - return - } - switch result { - case let .progress(progress): - updateProgressImpl?(progress) - case .completed: - Queue.mainQueue().after(0.1) { - self.isEditingStory = false - self.rewindCurrentItem() - self.updateIsProgressPaused() - self.state?.updated(transition: .easeInOut(duration: 0.2)) - - HapticFeedback().success() - - commit({}) - } - } - })) + + if case let .user(user) = peer { + externalState.isPeerArchived = user.storiesHidden ?? false + + } else if case let .channel(channel) = peer { + externalState.isPeerArchived = channel.storiesHidden ?? false } - case let .video(content, firstFrameImage, values, duration, dimensions): - updateProgressImpl?(0.0) - if let valuesData = try? JSONEncoder().encode(values) { - let data = MemoryBuffer(data: valuesData) - let digest = MemoryBuffer(data: data.md5Digest()) - let adjustments = VideoMediaResourceAdjustments(data: data, digest: digest, isStory: true) - - let resource: TelegramMediaResource - switch content { - case let .imageFile(path): - resource = LocalFileVideoMediaResource(randomId: Int64.random(in: .min ... .max), path: path, adjustments: adjustments) - case let .videoFile(path): - resource = LocalFileVideoMediaResource(randomId: Int64.random(in: .min ... .max), path: path, adjustments: adjustments) - case let .asset(localIdentifier): - resource = VideoLibraryMediaResource(localIdentifier: localIdentifier, conversion: .compress(adjustments)) + let forwardInfo = Stories.PendingForwardInfo(peerId: component.slice.peer.id, storyId: item.id, isModified: result.media != nil) + + if let rootController = context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface { + var existingMedia: EngineMedia? + if let _ = result.media { + } else { + existingMedia = item.media } + rootController.proceedWithStoryUpload(target: target, result: result as! MediaEditorScreenResult, existingMedia: existingMedia, forwardInfo: forwardInfo, externalState: externalState, commit: commit) + } + }) + } else { + var updatedText: String? + var updatedEntities: [MessageTextEntity]? + if result.caption.string != item.text || entities != item.entities { + updatedText = result.caption.string + updatedEntities = entities + } + + if let mediaResult = result.media { + switch mediaResult { + case let .image(image, dimensions): + updateProgressImpl?(0.0) let tempFile = TempBox.shared.tempFile(fileName: "file") defer { TempBox.shared.dispose(tempFile) } - let firstFrameImageData = firstFrameImage.flatMap { compressImageToJPEG($0, quality: 0.6, tempFilePath: tempFile.path) } - let firstFrameFile = firstFrameImageData.flatMap { data -> TempBoxFile? in - let file = TempBox.shared.tempFile(fileName: "image.jpg") - if let _ = try? data.write(to: URL(fileURLWithPath: file.path)) { - return file - } else { - return nil - } + if let imageData = compressImageToJPEG(image, quality: 0.7, tempFilePath: tempFile.path) { + updateDisposable.set((context.engine.messages.editStory(peerId: peerId, id: id, media: .image(dimensions: dimensions, data: imageData, stickers: result.stickers), mediaAreas: result.mediaAreas, text: updatedText, entities: updatedEntities, privacy: nil) + |> deliverOnMainQueue).startStrict(next: { [weak self] result in + guard let self else { + return + } + switch result { + case let .progress(progress): + updateProgressImpl?(progress) + case .completed: + Queue.mainQueue().after(0.1) { + self.isEditingStory = false + self.rewindCurrentItem() + self.updateIsProgressPaused() + self.state?.updated(transition: .easeInOut(duration: 0.2)) + + HapticFeedback().success() + + commit({}) + } + } + })) } + case let .video(content, firstFrameImage, values, duration, dimensions): + updateProgressImpl?(0.0) - updateDisposable.set((context.engine.messages.editStory(peerId: peerId, id: id, media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameFile: firstFrameFile, stickers: stickers), mediaAreas: mediaAreas, text: updatedText, entities: updatedEntities, privacy: nil) - |> deliverOnMainQueue).startStrict(next: { [weak self] result in - guard let self else { - return + if let valuesData = try? JSONEncoder().encode(values) { + let data = MemoryBuffer(data: valuesData) + let digest = MemoryBuffer(data: data.md5Digest()) + let adjustments = VideoMediaResourceAdjustments(data: data, digest: digest, isStory: true) + + let resource: TelegramMediaResource + switch content { + case let .imageFile(path): + resource = LocalFileVideoMediaResource(randomId: Int64.random(in: .min ... .max), path: path, adjustments: adjustments) + case let .videoFile(path): + resource = LocalFileVideoMediaResource(randomId: Int64.random(in: .min ... .max), path: path, adjustments: adjustments) + case let .asset(localIdentifier): + resource = VideoLibraryMediaResource(localIdentifier: localIdentifier, conversion: .compress(adjustments)) } - switch result { - case let .progress(progress): - updateProgressImpl?(progress) - case .completed: - Queue.mainQueue().after(0.1) { + + let tempFile = TempBox.shared.tempFile(fileName: "file") + defer { + TempBox.shared.dispose(tempFile) + } + let firstFrameImageData = firstFrameImage.flatMap { compressImageToJPEG($0, quality: 0.6, tempFilePath: tempFile.path) } + let firstFrameFile = firstFrameImageData.flatMap { data -> TempBoxFile? in + let file = TempBox.shared.tempFile(fileName: "image.jpg") + if let _ = try? data.write(to: URL(fileURLWithPath: file.path)) { + return file + } else { + return nil + } + } + + updateDisposable.set((context.engine.messages.editStory(peerId: peerId, id: id, media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameFile: firstFrameFile, stickers: result.stickers), mediaAreas: result.mediaAreas, text: updatedText, entities: updatedEntities, privacy: nil) + |> deliverOnMainQueue).startStrict(next: { [weak self] result in + guard let self else { + return + } + switch result { + case let .progress(progress): + updateProgressImpl?(progress) + case .completed: + Queue.mainQueue().after(0.1) { + self.isEditingStory = false + self.rewindCurrentItem() + self.updateIsProgressPaused() + self.state?.updated(transition: .easeInOut(duration: 0.2)) + + HapticFeedback().success() + + commit({}) + } + } + })) + } + } + } else if updatedText != nil { + let _ = (context.engine.messages.editStory(peerId: peerId, id: id, media: nil, mediaAreas: nil, text: updatedText, entities: updatedEntities, privacy: nil) + |> deliverOnMainQueue).startStandalone(next: { [weak self] result in + switch result { + case .completed: + Queue.mainQueue().after(0.1) { + if let self { self.isEditingStory = false self.rewindCurrentItem() self.updateIsProgressPaused() self.state?.updated(transition: .easeInOut(duration: 0.2)) HapticFeedback().success() - - commit({}) } + commit({}) } - })) - } - } - } else if updatedText != nil { - let _ = (context.engine.messages.editStory(peerId: peerId, id: id, media: nil, mediaAreas: nil, text: updatedText, entities: updatedEntities, privacy: nil) - |> deliverOnMainQueue).startStandalone(next: { [weak self] result in - switch result { - case .completed: - Queue.mainQueue().after(0.1) { - if let self { - self.isEditingStory = false - self.rewindCurrentItem() - self.updateIsProgressPaused() - self.state?.updated(transition: .easeInOut(duration: 0.2)) - - HapticFeedback().success() - } - commit({}) + default: + break } - default: - break - } - }) - } else { - self.isEditingStory = false - self.rewindCurrentItem() - self.updateIsProgressPaused() - self.state?.updated(transition: .easeInOut(duration: 0.2)) - - HapticFeedback().success() - - commit({}) + }) + } else { + self.isEditingStory = false + self.rewindCurrentItem() + self.updateIsProgressPaused() + self.state?.updated(transition: .easeInOut(duration: 0.2)) + + HapticFeedback().success() + + commit({}) + } } - } ) controller.willDismiss = { [weak self] in @@ -5577,6 +5739,8 @@ public final class StoryItemSetContainerComponent: Component { c.dismiss() } else if let c = c as? TooltipScreen { c.dismiss() + } else if let c = c as? TooltipController { + c.dismiss() } return true } @@ -5954,6 +6118,28 @@ public final class StoryItemSetContainerComponent: Component { }))) } + if component.slice.additionalPeerData.canViewStats { + items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_ViewStats, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Statistics"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, a in + a(.default) + + guard let self, let component = self.component else { + return + } + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkColorPresentationTheme) + let statsController = component.context.sharedContext.makeStoryStatsController( + context: component.context, + updatedPresentationData: (presentationData, .single(presentationData)), + peerId: component.slice.peer.id, + storyId: component.slice.item.storyItem.id, + storyItem: component.slice.item.storyItem, + fromStory: true + ) + component.controller()?.push(statsController) + }))) + } + let saveText: String = component.strings.Story_Context_SaveToGallery items.append(.action(ContextMenuActionItem(text: saveText, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.contextMenu.primaryColor) @@ -6335,6 +6521,28 @@ public final class StoryItemSetContainerComponent: Component { }))) } + if component.slice.additionalPeerData.canViewStats { + items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_ViewStats, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Statistics"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, a in + a(.default) + + guard let self, let component = self.component else { + return + } + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkColorPresentationTheme) + let statsController = component.context.sharedContext.makeStoryStatsController( + context: component.context, + updatedPresentationData: (presentationData, .single(presentationData)), + peerId: component.slice.peer.id, + storyId: component.slice.item.storyItem.id, + storyItem: component.slice.item.storyItem, + fromStory: true + ) + component.controller()?.push(statsController) + }))) + } + if !component.slice.item.storyItem.text.isEmpty { let (canTranslate, _) = canTranslateText(context: component.context, text: component.slice.item.storyItem.text, showTranslate: translationSettings.showTranslate, showTranslateIfTopical: false, ignoredLanguages: translationSettings.ignoredLanguages) if canTranslate { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index 7481c9f2222..832c3c040a9 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -1041,7 +1041,14 @@ final class StoryItemSetContainerSendMessage { immediateExternalShare: false, forceTheme: defaultDarkColorPresentationTheme ) - + if !component.slice.peer.isService { + shareController.shareStory = { [weak view] in + guard let view else { + return + } + view.openStoryEditing(repost: true) + } + } shareController.completed = { [weak view] peerIds in guard let view, let component = view.component else { return @@ -1052,7 +1059,7 @@ final class StoryItemSetContainerSendMessage { peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init) ) ) - |> deliverOnMainQueue).start(next: { [weak view] peerList in + |> deliverOnMainQueue).start(next: { [weak view] peerList in guard let view, let component = view.component else { return } @@ -2616,7 +2623,7 @@ final class StoryItemSetContainerSendMessage { |> deliverOnMainQueue).start() } - func openResolved(view: StoryItemSetContainerComponent.View, result: ResolvedUrl, forceExternal: Bool = false, concealed: Bool = false) { + func openResolved(view: StoryItemSetContainerComponent.View, result: ResolvedUrl, forceExternal: Bool = false, concealed: Bool = false, completion: (() -> Void)? = nil) { guard let component = view.component, let navigationController = component.controller()?.navigationController as? NavigationController else { return } @@ -2710,7 +2717,8 @@ final class StoryItemSetContainerSendMessage { view.endEditing(true) }, contentContext: self.progressPauseContext, - progress: nil + progress: nil, + completion: completion ) } @@ -2790,7 +2798,7 @@ final class StoryItemSetContainerSendMessage { if let peer = peer { var navigation: ChatControllerInteractionNavigateToPeer if let peer = peer as? TelegramUser, peer.botInfo == nil { - navigation = .info + navigation = .info(nil) } else { navigation = .chat(textInputState: nil, subject: nil, peekData: nil) } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetViewListComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetViewListComponent.swift index 12f9512f23e..a99e63a9c5f 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetViewListComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetViewListComponent.swift @@ -231,8 +231,9 @@ final class StoryItemSetViewListComponent: Component { } private enum SortMode: Int { - case reactionsFirst = 0 - case recentFirst = 1 + case repostsFirst = 0 + case reactionsFirst = 1 + case recentFirst = 2 } private struct ContentConfigurationKey: Equatable { @@ -688,6 +689,8 @@ final class StoryItemSetViewListComponent: Component { } let mappedSortMode: EngineStoryViewListContext.SortMode switch self.configuration.sortMode { + case .repostsFirst: + mappedSortMode = .repostsFirst case .reactionsFirst: mappedSortMode = .reactionsFirst case .recentFirst: @@ -725,6 +728,8 @@ final class StoryItemSetViewListComponent: Component { } let mappedSortMode: EngineStoryViewListContext.SortMode switch self.configuration.sortMode { + case .repostsFirst: + mappedSortMode = .repostsFirst case .reactionsFirst: mappedSortMode = .reactionsFirst case .recentFirst: @@ -1288,6 +1293,24 @@ final class StoryItemSetViewListComponent: Component { let sortMode = self.sortMode +// items.append(.action(ContextMenuActionItem(text: component.strings.Story_ViewList_ContextSortReposts, icon: { theme in +// return generateTintedImage(image: UIImage(bundleImageName: "Stories/Context Menu/Repost"), color: theme.contextMenu.primaryColor) +// }, additionalLeftIcon: { theme in +// if sortMode != .repostsFirst { +// return nil +// } +// return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) +// }, action: { [weak self] _, a in +// a(.default) +// +// guard let self else { +// return +// } +// if self.sortMode != .repostsFirst { +// self.sortMode = .repostsFirst +// self.state?.updated(transition: .immediate) +// } +// }))) items.append(.action(ContextMenuActionItem(text: component.strings.Story_ViewList_ContextSortReactions, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reactions"), color: theme.contextMenu.primaryColor) }, additionalLeftIcon: { theme in @@ -1525,6 +1548,15 @@ final class StoryItemSetViewListComponent: Component { containerSize: CGSize(width: 260.0, height: 100.0) ) + let orderSelectorIconName: String + switch self.sortMode { + case .repostsFirst: + orderSelectorIconName = "Stories/Context Menu/Repost" + case .reactionsFirst: + orderSelectorIconName = "Chat/Context Menu/Reactions" + case .recentFirst: + orderSelectorIconName = "Chat/Context Menu/Time" + } let orderSelectorSize = self.orderSelector.update( transition: transition, component: AnyComponent(OptionButtonComponent( @@ -1532,7 +1564,7 @@ final class StoryItemSetViewListComponent: Component { background: UIColor(rgb: 0xffffff, alpha: 0.09), foreground: .white ), - icon: self.sortMode == .recentFirst ? "Chat/Context Menu/Time" : "Chat/Context Menu/Reactions", + icon: orderSelectorIconName, action: { [weak self] in guard let self else { return diff --git a/submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent/Sources/StoryFooterPanelComponent.swift b/submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent/Sources/StoryFooterPanelComponent.swift index f178635b822..2ff0324c261 100644 --- a/submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent/Sources/StoryFooterPanelComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent/Sources/StoryFooterPanelComponent.swift @@ -41,6 +41,7 @@ public final class StoryFooterPanelComponent: Component { public let storyItem: EngineStoryItem public let myReaction: MyReaction? public let isChannel: Bool + public let canShare: Bool public let externalViews: EngineStoryItem.Views? public let expandFraction: CGFloat public let expandViewStats: () -> Void @@ -48,6 +49,8 @@ public final class StoryFooterPanelComponent: Component { public let moreAction: (UIView, ContextGesture?) -> Void public let likeAction: () -> Void public let forwardAction: () -> Void + public let repostAction: () -> Void + public let cancelUploadAction: () -> Void public init( context: AccountContext, @@ -56,13 +59,16 @@ public final class StoryFooterPanelComponent: Component { storyItem: EngineStoryItem, myReaction: MyReaction?, isChannel: Bool, + canShare: Bool, externalViews: EngineStoryItem.Views?, expandFraction: CGFloat, expandViewStats: @escaping () -> Void, deleteAction: @escaping () -> Void, moreAction: @escaping (UIView, ContextGesture?) -> Void, likeAction: @escaping () -> Void, - forwardAction: @escaping () -> Void + forwardAction: @escaping () -> Void, + repostAction: @escaping () -> Void, + cancelUploadAction: @escaping () -> Void ) { self.context = context self.theme = theme @@ -70,6 +76,7 @@ public final class StoryFooterPanelComponent: Component { self.storyItem = storyItem self.myReaction = myReaction self.isChannel = isChannel + self.canShare = canShare self.externalViews = externalViews self.expandViewStats = expandViewStats self.expandFraction = expandFraction @@ -77,6 +84,8 @@ public final class StoryFooterPanelComponent: Component { self.moreAction = moreAction self.likeAction = likeAction self.forwardAction = forwardAction + self.repostAction = repostAction + self.cancelUploadAction = cancelUploadAction } public static func ==(lhs: StoryFooterPanelComponent, rhs: StoryFooterPanelComponent) -> Bool { @@ -116,10 +125,15 @@ public final class StoryFooterPanelComponent: Component { private var likeButton: ComponentView? private var likeStatsText: AnimatedCountLabelView? private var forwardButton: ComponentView? + private var repostButton: ComponentView? + private var forwardStatsText: AnimatedCountLabelView? private var reactionStatsIcon: UIImageView? private var reactionStatsText: AnimatedCountLabelView? + private var repostStatsIcon: UIImageView? + private var repostStatsText: AnimatedCountLabelView? + private var statusButton: HighlightableButton? private var statusNode: SemanticStatusNode? private var uploadingText: ComponentView? @@ -174,18 +188,24 @@ public final class StoryFooterPanelComponent: Component { self.viewStatsLabelText.view?.alpha = 0.7 self.reactionStatsIcon?.alpha = 0.7 self.reactionStatsText?.alpha = 0.7 + self.repostStatsIcon?.alpha = 0.7 + self.repostStatsText?.alpha = 0.7 } else { self.avatarsView.alpha = 1.0 self.viewStatsCountText.alpha = 1.0 self.viewStatsLabelText.view?.alpha = 1.0 self.reactionStatsIcon?.alpha = 1.0 self.reactionStatsText?.alpha = 1.0 + self.repostStatsIcon?.alpha = 1.0 + self.repostStatsText?.alpha = 1.0 self.avatarsView.layer.animateAlpha(from: 0.7, to: 1.0, duration: 0.2) self.viewStatsCountText.layer.animateAlpha(from: 0.7, to: 1.0, duration: 0.2) self.viewStatsLabelText.view?.layer.animateAlpha(from: 0.7, to: 1.0, duration: 0.2) self.reactionStatsIcon?.layer.animateAlpha(from: 0.7, to: 1.0, duration: 0.2) self.reactionStatsText?.layer.animateAlpha(from: 0.7, to: 1.0, duration: 0.2) + self.repostStatsIcon?.layer.animateAlpha(from: 0.7, to: 1.0, duration: 0.2) + self.repostStatsText?.layer.animateAlpha(from: 0.7, to: 1.0, duration: 0.2) } } self.viewStatsButton.addTarget(self, action: #selector(self.viewStatsPressed), for: .touchUpInside) @@ -214,7 +234,7 @@ public final class StoryFooterPanelComponent: Component { guard let component = self.component else { return } - component.context.engine.messages.cancelStoryUpload(stableId: component.storyItem.id) + component.cancelUploadAction() } func update(component: StoryFooterPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { @@ -349,9 +369,11 @@ public final class StoryFooterPanelComponent: Component { var viewCount = 0 var reactionCount = 0 + var forwardCount = 0 if let views = component.externalViews ?? component.storyItem.views, views.seenCount != 0 { viewCount = views.seenCount reactionCount = views.reactedCount + forwardCount = views.forwardCount } if component.isChannel { @@ -367,9 +389,11 @@ public final class StoryFooterPanelComponent: Component { if component.isChannel { var likeStatsTransition = transition + var forwardStatsTransition = transition if transition.animation.isImmediate, !isFirstTime, let previousComponent, previousComponent.storyItem.id == component.storyItem.id, previousComponent.expandFraction == component.expandFraction { likeStatsTransition = .easeInOut(duration: 0.2) + forwardStatsTransition = .easeInOut(duration: 0.2) } let likeStatsText: AnimatedCountLabelView @@ -413,15 +437,7 @@ public final class StoryFooterPanelComponent: Component { likeButton = ComponentView() self.likeButton = likeButton } - - let forwardButton: ComponentView - if let current = self.forwardButton { - forwardButton = current - } else { - forwardButton = ComponentView() - self.forwardButton = forwardButton - } - + let likeButtonSize = likeButton.update( transition: likeStatsTransition, component: AnyComponent(MessageInputActionButtonComponent( @@ -477,58 +493,171 @@ public final class StoryFooterPanelComponent: Component { rightContentOffset -= likeButtonSize.width + 14.0 } - let forwardButtonSize = forwardButton.update( - transition: likeStatsTransition, - component: AnyComponent(MessageInputActionButtonComponent( - mode: .forward, - storyId: component.storyItem.id, - action: { [weak self] _, action, _ in - guard let self, let component = self.component else { - return - } - guard case .up = action else { - return - } - component.forwardAction() - }, - longPressAction: nil, - switchMediaInputMode: { - }, - updateMediaCancelFraction: { _ in - }, - lockMediaRecording: { - }, - stopAndPreviewMediaRecording: { - }, - moreAction: { _, _ in }, - context: component.context, - theme: component.theme, - strings: component.strings, - presentController: { _ in }, - audioRecorder: nil, - videoRecordingStatus: nil - )), - environment: {}, - containerSize: CGSize(width: 33.0, height: 33.0) - ) - if let forwardButtonView = forwardButton.view { - if forwardButtonView.superview == nil { - self.addSubview(forwardButtonView) + if component.canShare { + let forwardStatsText: AnimatedCountLabelView + if let current = self.forwardStatsText { + forwardStatsText = current + } else { + forwardStatsTransition = forwardStatsTransition.withAnimation(.none) + forwardStatsText = AnimatedCountLabelView(frame: CGRect()) + forwardStatsText.isUserInteractionEnabled = false + self.forwardStatsText = forwardStatsText + self.externalContainerView.addSubview(forwardStatsText) + } + + let forwardStatsLayout = forwardStatsText.update( + size: CGSize(width: availableSize.width, height: size.height), + segments: [ + .number(forwardCount, NSAttributedString(string: "\(forwardCount)", font: Font.with(size: 15.0, traits: .monospacedNumbers), textColor: .white)) + ], + transition: (isFirstTime || likeStatsTransition.animation.isImmediate) ? .immediate : ContainedViewLayoutTransition.animated(duration: 0.25, curve: .easeInOut) + ) + var forwardStatsFrame = CGRect(origin: CGPoint(x: rightContentOffset - forwardStatsLayout.size.width, y: floor((size.height - forwardStatsLayout.size.height) * 0.5)), size: forwardStatsLayout.size) + forwardStatsFrame.origin.y += component.expandFraction * 45.0 + + forwardStatsTransition.setPosition(view: forwardStatsText, position: forwardStatsFrame.center) + forwardStatsTransition.setBounds(view: forwardStatsText, bounds: CGRect(origin: CGPoint(), size: forwardStatsFrame.size)) + var forwardStatsAlpha: CGFloat = (1.0 - component.expandFraction) + if forwardCount == 0 { + forwardStatsAlpha = 0.0 + } + forwardStatsTransition.setAlpha(view: forwardStatsText, alpha: forwardStatsAlpha) + forwardStatsTransition.setScale(view: forwardStatsText, scale: forwardCount == 0 ? 0.001 : 1.0) + + if forwardCount != 0 { + rightContentOffset -= forwardStatsLayout.size.width + 1.0 + } + + let repostButton: ComponentView + if let current = self.repostButton { + repostButton = current + } else { + repostButton = ComponentView() + self.repostButton = repostButton } - var forwardButtonFrame = CGRect(origin: CGPoint(x: rightContentOffset - likeButtonSize.width, y: floor((size.height - forwardButtonSize.height) * 0.5)), size: forwardButtonSize) - forwardButtonFrame.origin.y += component.expandFraction * 45.0 - likeStatsTransition.setPosition(view: forwardButtonView, position: forwardButtonFrame.center) - likeStatsTransition.setBounds(view: forwardButtonView, bounds: CGRect(origin: CGPoint(), size: forwardButtonFrame.size)) - likeStatsTransition.setAlpha(view: forwardButtonView, alpha: 1.0 - component.expandFraction) + let forwardButton: ComponentView + if let current = self.forwardButton { + forwardButton = current + } else { + forwardButton = ComponentView() + self.forwardButton = forwardButton + } + + let repostButtonSize = repostButton.update( + transition: likeStatsTransition, + component: AnyComponent(MessageInputActionButtonComponent( + mode: .repost, + storyId: component.storyItem.id, + action: { [weak self] _, action, _ in + guard let self, let component = self.component else { + return + } + guard case .up = action else { + return + } + component.repostAction() + }, + longPressAction: nil, + switchMediaInputMode: { + }, + updateMediaCancelFraction: { _ in + }, + lockMediaRecording: { + }, + stopAndPreviewMediaRecording: { + }, + moreAction: { _, _ in }, + context: component.context, + theme: component.theme, + strings: component.strings, + presentController: { _ in }, + audioRecorder: nil, + videoRecordingStatus: nil + )), + environment: {}, + containerSize: CGSize(width: 33.0, height: 33.0) + ) + if let repostButtonView = repostButton.view { + if repostButtonView.superview == nil { + self.addSubview(repostButtonView) + } + var repostButtonFrame = CGRect(origin: CGPoint(x: rightContentOffset - repostButtonSize.width, y: floor((size.height - repostButtonSize.height) * 0.5)), size: repostButtonSize) + repostButtonFrame.origin.y += component.expandFraction * 45.0 + + likeStatsTransition.setPosition(view: repostButtonView, position: repostButtonFrame.center) + likeStatsTransition.setBounds(view: repostButtonView, bounds: CGRect(origin: CGPoint(), size: repostButtonFrame.size)) + likeStatsTransition.setAlpha(view: repostButtonView, alpha: 1.0 - component.expandFraction) + + rightContentOffset -= repostButtonSize.width + 14.0 + } - rightContentOffset -= forwardButtonSize.width + 8.0 + let forwardButtonSize = forwardButton.update( + transition: likeStatsTransition, + component: AnyComponent(MessageInputActionButtonComponent( + mode: .forward, + storyId: component.storyItem.id, + action: { [weak self] _, action, _ in + guard let self, let component = self.component else { + return + } + guard case .up = action else { + return + } + component.forwardAction() + }, + longPressAction: nil, + switchMediaInputMode: { + }, + updateMediaCancelFraction: { _ in + }, + lockMediaRecording: { + }, + stopAndPreviewMediaRecording: { + }, + moreAction: { _, _ in }, + context: component.context, + theme: component.theme, + strings: component.strings, + presentController: { _ in }, + audioRecorder: nil, + videoRecordingStatus: nil + )), + environment: {}, + containerSize: CGSize(width: 33.0, height: 33.0) + ) + if let forwardButtonView = forwardButton.view { + if forwardButtonView.superview == nil { + self.addSubview(forwardButtonView) + } + var forwardButtonFrame = CGRect(origin: CGPoint(x: rightContentOffset - likeButtonSize.width, y: floor((size.height - forwardButtonSize.height) * 0.5)), size: forwardButtonSize) + forwardButtonFrame.origin.y += component.expandFraction * 45.0 + + likeStatsTransition.setPosition(view: forwardButtonView, position: forwardButtonFrame.center) + likeStatsTransition.setBounds(view: forwardButtonView, bounds: CGRect(origin: CGPoint(), size: forwardButtonFrame.size)) + likeStatsTransition.setAlpha(view: forwardButtonView, alpha: 1.0 - component.expandFraction) + + rightContentOffset -= forwardButtonSize.width + 8.0 + } + } else { + if let repostButton = self.repostButton { + self.repostButton = nil + repostButton.view?.removeFromSuperview() + } + if let forwardButton = self.forwardButton { + self.forwardButton = nil + forwardButton.view?.removeFromSuperview() + } } } else { if let likeButton = self.likeButton { self.likeButton = nil likeButton.view?.removeFromSuperview() } + if let repostButton = self.repostButton { + self.repostButton = nil + repostButton.view?.removeFromSuperview() + } if let forwardButton = self.forwardButton { self.forwardButton = nil forwardButton.view?.removeFromSuperview() @@ -571,6 +700,9 @@ public final class StoryFooterPanelComponent: Component { var reactionsIconSize: CGSize? var reactionsTextSize: CGSize? + var repostsIconSize: CGSize? + var repostsTextSize: CGSize? + if reactionCount != 0 && !component.isChannel { var reactionsTransition = transition let reactionStatsIcon: UIImageView @@ -625,6 +757,60 @@ public final class StoryFooterPanelComponent: Component { } } + if forwardCount != 0 && !component.isChannel { + var repostTransition = transition + let repostStatsIcon: UIImageView + if let current = self.repostStatsIcon { + repostStatsIcon = current + } else { + repostTransition = repostTransition.withAnimation(.none) + repostStatsIcon = UIImageView() + repostStatsIcon.image = UIImage(bundleImageName: "Stories/InputRepost")?.withRenderingMode(.alwaysTemplate) + + self.repostStatsIcon = repostStatsIcon + self.externalContainerView.addSubview(repostStatsIcon) + } + + transition.setTintColor(view: repostStatsIcon, color: UIColor(rgb: 0x34c759).mixedWith(.white, alpha: component.expandFraction)) + + let repostStatsText: AnimatedCountLabelView + if let current = self.repostStatsText { + repostStatsText = current + } else { + repostStatsText = AnimatedCountLabelView(frame: CGRect()) + repostStatsText.isUserInteractionEnabled = false + self.repostStatsText = repostStatsText + self.externalContainerView.addSubview(repostStatsText) + } + + let repostStatsLayout = repostStatsText.update( + size: CGSize(width: availableSize.width, height: size.height), + segments: [ + .number(forwardCount, NSAttributedString(string: "\(forwardCount)", font: Font.with(size: 15.0, traits: .monospacedNumbers), textColor: .white)) + ], + reducedLetterSpacing: true, + transition: (isFirstTime || repostTransition.animation.isImmediate) ? .immediate : ContainedViewLayoutTransition.animated(duration: 0.25, curve: .easeInOut) + ) + repostsTextSize = repostStatsLayout.size + + let imageSize = CGSize(width: 23.0, height: 23.0) + repostsIconSize = imageSize + } else { + if let repostStatsIcon = self.repostStatsIcon { + self.repostStatsIcon = nil + repostStatsIcon.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak repostStatsIcon] _ in + repostStatsIcon?.removeFromSuperview() + }) + } + + if let repostStatsText = self.repostStatsText { + self.repostStatsText = nil + repostStatsText.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak repostStatsText] _ in + repostStatsText?.removeFromSuperview() + }) + } + } + let viewsReactionsCollapsedSpacing: CGFloat = 6.0 let viewsReactionsExpandedSpacing: CGFloat = 8.0 let viewsReactionsSpacing = viewsReactionsCollapsedSpacing.interpolate(to: viewsReactionsExpandedSpacing, amount: component.expandFraction) @@ -668,6 +854,12 @@ public final class StoryFooterPanelComponent: Component { contentWidth += reactionsIconSpacing contentWidth += reactionsTextSize.width } + if let repostsIconSize, let repostsTextSize { + contentWidth += viewsReactionsSpacing + contentWidth += repostsIconSize.width + contentWidth += reactionsIconSpacing + contentWidth += repostsTextSize.width + } } let minContentX: CGFloat = 16.0 @@ -747,6 +939,17 @@ public final class StoryFooterPanelComponent: Component { contentX += reactionsTextSize.width } + if let repostStatsIcon = self.repostStatsIcon, let repostsIconSize, let repostStatsText = self.repostStatsText, let repostsTextSize { + contentX += viewsReactionsSpacing + + transition.setFrame(view: repostStatsIcon, frame: CGRect(origin: CGPoint(x: contentX, y: floor((size.height - repostsIconSize.height) * 0.5)), size: repostsIconSize)) + contentX += repostsIconSize.width + contentX += reactionsIconSpacing + + transition.setFrame(view: repostStatsText, frame: CGRect(origin: CGPoint(x: contentX, y: floor((size.height - repostsTextSize.height) * 0.5)), size: repostsTextSize)) + contentX += repostsTextSize.width + } + let statsButtonWidth = availableSize.width - 80.0 transition.setFrame(view: self.viewStatsButton, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: statsButtonWidth, height: baseHeight))) @@ -768,7 +971,11 @@ public final class StoryFooterPanelComponent: Component { guard let self, let component = self.component else { return } - component.deleteAction() + if component.storyItem.isPending { + component.cancelUploadAction() + } else { + component.deleteAction() + } } ).minSize(CGSize(width: 44.0, height: baseHeight))), environment: {}, diff --git a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift index 09ff3c657be..d0af5a34fc6 100644 --- a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift @@ -383,6 +383,8 @@ public final class StoryPeerListComponent: Component { super.init(frame: frame) + self.disablesInteractiveTransitionGestureRecognizer = true + self.scrollView.delegate = self self.scrollView.alpha = 0.0 self.scrollContainerView.addGestureRecognizer(self.scrollView.panGestureRecognizer) diff --git a/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift b/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift index c6fa45b0f24..d8d5e02e035 100644 --- a/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift +++ b/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift @@ -19,9 +19,11 @@ public final class TabSelectorComponent: Component { } public struct CustomLayout: Equatable { + public var font: UIFont public var spacing: CGFloat - public init(spacing: CGFloat) { + public init(font: UIFont, spacing: CGFloat) { + self.font = font self.spacing = spacing } } @@ -113,8 +115,8 @@ public final class TabSelectorComponent: Component { let spacing: CGFloat = component.customLayout?.spacing ?? 2.0 let itemFont: UIFont - if component.customLayout != nil { - itemFont = Font.medium(14.0) + if let customLayout = component.customLayout { + itemFont = customLayout.font } else { itemFont = Font.semibold(14.0) } diff --git a/submodules/TelegramUI/Components/TextFieldComponent/BUILD b/submodules/TelegramUI/Components/TextFieldComponent/BUILD index db908db94c0..045b43ad01f 100644 --- a/submodules/TelegramUI/Components/TextFieldComponent/BUILD +++ b/submodules/TelegramUI/Components/TextFieldComponent/BUILD @@ -21,6 +21,9 @@ swift_library( "//submodules/ChatTextLinkEditUI", "//submodules/Pasteboard", "//submodules/ImageTransparency", + "//submodules/TelegramUI/Components/Chat/ChatInputTextNode", + "//submodules/TextInputMenu", + "//submodules/ObjCRuntimeUtils", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/TextFieldComponent/Sources/TextFieldComponent.swift b/submodules/TelegramUI/Components/TextFieldComponent/Sources/TextFieldComponent.swift index f7824966c89..a4779139ada 100644 --- a/submodules/TelegramUI/Components/TextFieldComponent/Sources/TextFieldComponent.swift +++ b/submodules/TelegramUI/Components/TextFieldComponent/Sources/TextFieldComponent.swift @@ -13,6 +13,9 @@ import Pasteboard import ChatTextLinkEditUI import MobileCoreServices import ImageTransparency +import ChatInputTextNode +import TextInputMenu +import ObjCRuntimeUtils public final class EmptyInputView: UIView, UIInputViewAudioFeedback { public var enableInputClicksWhenVisible: Bool { @@ -180,28 +183,9 @@ public final class TextFieldComponent: Component { } } - final class TextView: UITextView { - var onPaste: () -> Bool = { return true } - - override func paste(_ sender: Any?) { - if self.onPaste() { - super.paste(sender) - } - } - - override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { - if action == #selector(self.paste(_:)) { - return true - } - return super.canPerformAction(action, withSender: sender) - } - } - - public final class View: UIView, UITextViewDelegate, UIScrollViewDelegate { - private let textContainer: NSTextContainer - private let textStorage: NSTextStorage - private let layoutManager: NSLayoutManager - private let textView: TextView + public final class View: UIView, UIScrollViewDelegate, ChatInputTextNodeDelegate { + private let textView: ChatInputTextView + private let inputMenu: TextInputMenu private var spoilerView: InvisibleInkDustView? private var customEmojiContainerView: CustomEmojiContainerView? @@ -216,22 +200,10 @@ public final class TextFieldComponent: Component { private var component: TextFieldComponent? private weak var state: EmptyComponentState? + private var isUpdating: Bool = false override init(frame: CGRect) { - self.textContainer = NSTextContainer(size: CGSize()) - self.textContainer.widthTracksTextView = false - self.textContainer.heightTracksTextView = false - self.textContainer.lineBreakMode = .byWordWrapping - self.textContainer.lineFragmentPadding = 8.0 - - self.textStorage = NSTextStorage() - - self.layoutManager = NSLayoutManager() - self.layoutManager.allowsNonContiguousLayout = false - self.layoutManager.addTextContainer(self.textContainer) - self.textStorage.addLayoutManager(self.layoutManager) - - self.textView = TextView(frame: CGRect(), textContainer: self.textContainer) + self.textView = ChatInputTextView(disableTiling: false) self.textView.translatesAutoresizingMaskIntoConstraints = false self.textView.backgroundColor = nil self.textView.layer.isOpaque = false @@ -239,16 +211,15 @@ public final class TextFieldComponent: Component { self.textView.indicatorStyle = .white self.textView.scrollIndicatorInsets = UIEdgeInsets(top: 9.0, left: 0.0, bottom: 9.0, right: 0.0) + self.inputMenu = TextInputMenu(hasSpoilers: true, hasQuotes: true) + super.init(frame: frame) self.clipsToBounds = true - self.textView.delegate = self + self.textView.customDelegate = self self.addSubview(self.textView) - self.textContainer.widthTracksTextView = false - self.textContainer.heightTracksTextView = false - if #available(iOS 13.0, *) { self.textView.overrideUserInterfaceStyle = .dark } @@ -257,10 +228,6 @@ public final class TextFieldComponent: Component { NSAttributedString.Key.font: Font.regular(17.0), NSAttributedString.Key.foregroundColor: UIColor.white ] - - self.textView.onPaste = { [weak self] in - return self?.onPaste() ?? false - } } required init?(coder: NSCoder) { @@ -274,12 +241,20 @@ public final class TextFieldComponent: Component { let inputState = f(self.inputState) - self.textView.attributedText = textAttributedStringForStateText(inputState.inputText, fontSize: component.fontSize, textColor: component.textColor, accentTextColor: component.textColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickers.keys), emojiViewProvider: self.emojiViewProvider) + let currentAttributedText = self.textView.attributedText + let updatedAttributedText = textAttributedStringForStateText(inputState.inputText, fontSize: component.fontSize, textColor: component.textColor, accentTextColor: component.textColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickers.keys), emojiViewProvider: self.emojiViewProvider) + if currentAttributedText != updatedAttributedText { + self.textView.attributedText = updatedAttributedText + } self.textView.selectedRange = NSMakeRange(inputState.selectionRange.lowerBound, inputState.selectionRange.count) refreshChatTextInputAttributes(textView: self.textView, primaryTextColor: component.textColor, accentTextColor: component.textColor, baseFontSize: component.fontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickers.keys), emojiViewProvider: self.emojiViewProvider) self.updateEntities() + + if currentAttributedText != updatedAttributedText && !self.isUpdating { + self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(kind: .textChanged))) + } } public func hasFirstResponder() -> Bool { @@ -290,7 +265,9 @@ public final class TextFieldComponent: Component { self.updateInputState { state in return state.insertText(text) } - self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(kind: .textChanged))) + if !self.isUpdating { + self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(kind: .textChanged))) + } } public func deleteBackward() { @@ -301,7 +278,9 @@ public final class TextFieldComponent: Component { self.updateInputState { _ in return TextFieldComponent.InputState(inputText: text, selectionRange: selectionRange) } - self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(kind: .textChanged))) + if !self.isUpdating { + self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(kind: .textChanged))) + } } private func onPaste() -> Bool { @@ -327,7 +306,9 @@ public final class TextFieldComponent: Component { return InputState(inputText: attributedString) } } - self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(kind: .textChanged))) + if !self.isUpdating { + self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(kind: .textChanged))) + } component.paste(.text) return false } @@ -382,12 +363,13 @@ public final class TextFieldComponent: Component { return true } - public func textViewDidChange(_ textView: UITextView) { + public func chatInputTextNodeDidUpdateText() { guard let component = self.component else { return } refreshChatTextInputAttributes(textView: self.textView, primaryTextColor: component.textColor, accentTextColor: component.textColor, baseFontSize: component.fontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickers.keys), emojiViewProvider: self.emojiViewProvider) refreshChatTextInputTypingAttributes(self.textView, textColor: component.textColor, baseFontSize: component.fontSize) + self.textView.updateTextContainerInset() if self.spoilerIsDisappearing { self.spoilerIsDisappearing = false @@ -395,11 +377,16 @@ public final class TextFieldComponent: Component { } self.updateEntities() - - self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(kind: .textChanged))) + if !self.isUpdating { + self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(kind: .textChanged))) + } } - public func textViewDidChangeSelection(_ textView: UITextView) { + public func chatInputTextNodeShouldReturn() -> Bool { + return true + } + + public func chatInputTextNodeDidChangeSelection(dueToEditing: Bool) { guard let _ = self.component else { return } @@ -408,11 +395,13 @@ public final class TextFieldComponent: Component { self.updateEmojiSuggestion(transition: .immediate) } - public func textViewDidBeginEditing(_ textView: UITextView) { + public func chatInputTextNodeDidBeginEditing() { guard let component = self.component else { return } - self.state?.updated(transition: Transition(animation: .curve(duration: 0.5, curve: .spring)).withUserData(AnimationHint(kind: .textFocusChanged))) + if !self.isUpdating { + self.state?.updated(transition: Transition(animation: .curve(duration: 0.5, curve: .spring)).withUserData(AnimationHint(kind: .textFocusChanged))) + } if component.isOneLineWhenUnfocused { Queue.mainQueue().justDispatch { self.textView.selectedTextRange = self.textView.textRange(from: self.textView.endOfDocument, to: self.textView.endOfDocument) @@ -420,12 +409,17 @@ public final class TextFieldComponent: Component { } } - public func textViewDidEndEditing(_ textView: UITextView) { - self.state?.updated(transition: Transition(animation: .curve(duration: 0.5, curve: .spring)).withUserData(AnimationHint(kind: .textFocusChanged))) + public func chatInputTextNodeDidFinishEditing() { + if !self.isUpdating { + self.state?.updated(transition: Transition(animation: .curve(duration: 0.5, curve: .spring)).withUserData(AnimationHint(kind: .textFocusChanged))) + } } - - @available(iOS 16.0, *) - public func textView(_ textView: UITextView, editMenuForTextIn range: NSRange, suggestedActions: [UIMenuElement]) -> UIMenu? { + + public func chatInputTextNodeBackspaceWhileEmpty() { + } + + @available(iOS 13.0, *) + public func chatInputTextNodeMenu(forTextRange textRange: NSRange, suggestedActions: [UIMenuElement]) -> UIMenu { let filteredActions: Set = Set([ "com.apple.menu.format", "com.apple.menu.replace" @@ -437,7 +431,7 @@ public final class TextFieldComponent: Component { return true } } - guard let component = self.component, !textView.attributedText.string.isEmpty && textView.selectedRange.length > 0 else { + guard let component = self.component, let attributedText = self.textView.attributedText, !attributedText.string.isEmpty, self.textView.selectedRange.length > 0 else { return UIMenu(children: suggestedActions) } let strings = component.strings @@ -504,6 +498,36 @@ public final class TextFieldComponent: Component { self.updateSpoilersRevealed(animated: animated) } }) + actions.insert(UIAction(title: strings.TextFormat_Quote, image: nil) { [weak self] action in + if let self { + var animated = false + let attributedText = self.inputState.inputText + attributedText.enumerateAttributes(in: NSMakeRange(0, attributedText.length), options: [], using: { attributes, _, _ in + if let _ = attributes[ChatTextInputAttributes.block] { + animated = true + } + }) + + self.toggleAttribute(key: ChatTextInputAttributes.block, value: ChatTextInputTextQuoteAttribute(kind: .quote)) + + self.updateSpoilersRevealed(animated: animated) + } + }, at: 0) + actions.append(UIAction(title: strings.TextFormat_Code, image: nil) { [weak self] action in + if let self { + var animated = false + let attributedText = self.inputState.inputText + attributedText.enumerateAttributes(in: NSMakeRange(0, attributedText.length), options: [], using: { attributes, _, _ in + if let _ = attributes[ChatTextInputAttributes.block] { + animated = true + } + }) + + self.toggleAttribute(key: ChatTextInputAttributes.block, value: ChatTextInputTextQuoteAttribute(kind: .code(language: nil))) + + self.updateSpoilersRevealed(animated: animated) + } + }) var updatedActions = suggestedActions let formatMenu = UIMenu(title: strings.TextFormat_Format, image: nil, children: actions) @@ -512,9 +536,148 @@ public final class TextFieldComponent: Component { return UIMenu(children: updatedActions) } - private func toggleAttribute(key: NSAttributedString.Key) { + public func chatInputTextNode(shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { + return true + } + + public func chatInputTextNodeShouldCopy() -> Bool { + return true + } + + public func chatInputTextNodeShouldPaste() -> Bool { + return self.onPaste() + } + + public func chatInputTextNodeShouldRespondToAction(action: Selector) -> Bool { + if action == #selector(self.paste(_:)) { + return true + } + return true + } + + public func chatInputTextNodeTargetForAction(action: Selector) -> ChatInputTextNode.TargetForAction? { + if action == makeSelectorFromString("_accessibilitySpeak:") { + if case .format = self.inputMenu.state { + return ChatInputTextNode.TargetForAction(target: nil) + } else if self.textView.selectedRange.length > 0 { + return ChatInputTextNode.TargetForAction(target: self) + } else { + return ChatInputTextNode.TargetForAction(target: nil) + } + } else if action == makeSelectorFromString("_accessibilitySpeakSpellOut:") { + if case .format = self.inputMenu.state { + return ChatInputTextNode.TargetForAction(target: nil) + } else if self.textView.selectedRange.length > 0 { + return nil + } else { + return ChatInputTextNode.TargetForAction(target: nil) + } + } + else if action == makeSelectorFromString("_accessibilitySpeakLanguageSelection:") || action == makeSelectorFromString("_accessibilityPauseSpeaking:") || action == makeSelectorFromString("_accessibilitySpeakSentence:") { + return ChatInputTextNode.TargetForAction(target: nil) + } else if action == makeSelectorFromString("_showTextStyleOptions:") { + if #available(iOS 16.0, *) { + return ChatInputTextNode.TargetForAction(target: nil) + } else { + if case .general = self.inputMenu.state { + if self.textView.attributedText == nil || self.textView.attributedText!.length == 0 { + return ChatInputTextNode.TargetForAction(target: nil) + } + return ChatInputTextNode.TargetForAction(target: self) + } else { + return ChatInputTextNode.TargetForAction(target: nil) + } + } + } else if action == #selector(self.formatAttributesBold(_:)) || action == #selector(self.formatAttributesItalic(_:)) || action == #selector(self.formatAttributesMonospace(_:)) || action == #selector(self.formatAttributesStrikethrough(_:)) || action == #selector(self.formatAttributesUnderline(_:)) || action == #selector(self.formatAttributesSpoiler(_:)) || action == #selector(self.formatAttributesQuote(_:)) || action == #selector(self.formatAttributesCodeBlock(_:)) { + if case .format = self.inputMenu.state { + if action == #selector(self.formatAttributesSpoiler(_:)) { + let selectedRange = self.textView.selectedRange + var intersectsMonospace = false + self.inputState.inputText.enumerateAttributes(in: selectedRange, options: [], using: { attributes, _, _ in + if let _ = attributes[ChatTextInputAttributes.monospace] { + intersectsMonospace = true + } + }) + if !intersectsMonospace { + return ChatInputTextNode.TargetForAction(target: self) + } else { + return ChatInputTextNode.TargetForAction(target: nil) + } + } else if action == #selector(self.formatAttributesQuote(_:)) { + return ChatInputTextNode.TargetForAction(target: self) + } else if action == #selector(self.formatAttributesCodeBlock(_:)) { + return ChatInputTextNode.TargetForAction(target: self) + } else if action == #selector(self.formatAttributesMonospace(_:)) { + var intersectsSpoiler = false + self.inputState.inputText.enumerateAttributes(in: self.textView.selectedRange, options: [], using: { attributes, _, _ in + if let _ = attributes[ChatTextInputAttributes.spoiler] { + intersectsSpoiler = true + } + }) + if !intersectsSpoiler { + return ChatInputTextNode.TargetForAction(target: self) + } else { + return ChatInputTextNode.TargetForAction(target: nil) + } + } else { + return ChatInputTextNode.TargetForAction(target: self) + } + } else { + return ChatInputTextNode.TargetForAction(target: nil) + } + } + if case .format = self.inputMenu.state { + return ChatInputTextNode.TargetForAction(target: nil) + } + return nil + } + + @objc func _showTextStyleOptions(_ sender: Any) { + let selectionRect: CGRect + if let selectedTextRange = self.textView.selectedTextRange { + selectionRect = self.textView.firstRect(for: selectedTextRange) + } else { + selectionRect = self.textView.bounds + } + + self.inputMenu.format(view: self.textView, rect: selectionRect.offsetBy(dx: 0.0, dy: -self.textView.contentOffset.y).insetBy(dx: 0.0, dy: -1.0)) + } + + @objc func formatAttributesBold(_ sender: Any) { + self.inputMenu.back() + } + + @objc func formatAttributesItalic(_ sender: Any) { + self.inputMenu.back() + } + + @objc func formatAttributesMonospace(_ sender: Any) { + self.inputMenu.back() + } + + @objc func formatAttributesStrikethrough(_ sender: Any) { + self.inputMenu.back() + } + + @objc func formatAttributesUnderline(_ sender: Any) { + self.inputMenu.back() + } + + @objc func formatAttributesQuote(_ sender: Any) { + self.inputMenu.back() + } + + @objc func formatAttributesCodeBlock(_ sender: Any) { + self.inputMenu.back() + } + + @objc func formatAttributesSpoiler(_ sender: Any) { + self.inputMenu.back() + } + + private func toggleAttribute(key: NSAttributedString.Key, value: Any? = nil) { self.updateInputState { state in - return state.addFormattingAttribute(attribute: key) + return state.addFormattingAttribute(attribute: key, value: value) } } @@ -563,7 +726,7 @@ public final class TextFieldComponent: Component { self.updateInputState { _ in return TextFieldComponent.InputState(inputText: string, selectionRange: string.length ..< string.length) } - if updateState { + if updateState && !self.isUpdating { self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(kind: .textChanged))) } } @@ -769,8 +932,8 @@ public final class TextFieldComponent: Component { var hasTracking = false var hasTrackingView = false - if self.textView.selectedRange.length == 0 && self.textView.selectedRange.location > 0 { - let selectedSubstring = self.textView.attributedText.attributedSubstring(from: NSRange(location: 0, length: self.textView.selectedRange.location)) + if let attributedText = self.textView.attributedText, self.textView.selectedRange.length == 0, self.textView.selectedRange.location > 0 { + let selectedSubstring = attributedText.attributedSubstring(from: NSRange(location: 0, length: self.textView.selectedRange.location)) if let lastCharacter = selectedSubstring.string.last, String(lastCharacter).isSingleEmoji { let queryLength = (String(lastCharacter) as NSString).length if selectedSubstring.attribute(ChatTextInputAttributes.customEmoji, at: selectedSubstring.length - queryLength, effectiveRange: nil) == nil { @@ -817,7 +980,7 @@ public final class TextFieldComponent: Component { } func rightmostPositionOfFirstLine() -> CGPoint? { - let glyphRange = self.layoutManager.glyphRange(for: self.textContainer) + let glyphRange = self.textView.layoutManager.glyphRange(for: self.textView.textContainer) if glyphRange.length == 0 { return nil } @@ -826,16 +989,16 @@ public final class TextFieldComponent: Component { var lineRange: NSRange = NSRange() repeat { - lineRect = self.layoutManager.lineFragmentUsedRect(forGlyphAt: glyphIndexForStringStart, effectiveRange: &lineRange) + lineRect = self.textView.layoutManager.lineFragmentUsedRect(forGlyphAt: glyphIndexForStringStart, effectiveRange: &lineRange) if NSMaxRange(lineRange) > glyphRange.length { lineRange.length = glyphRange.length - lineRange.location } glyphIndexForStringStart = NSMaxRange(lineRange) } while glyphIndexForStringStart < NSMaxRange(glyphRange) && !NSLocationInRange(glyphRange.location, lineRange) - let padding = self.textView.textContainerInset.left + let padding = self.textView.defaultTextContainerInset.left var rightmostX = lineRect.maxX + padding - let rightmostY = lineRect.minY + self.textView.textContainerInset.top + let rightmostY = lineRect.minY + self.textView.defaultTextContainerInset.top let nsString = (self.textView.text as NSString) let firstLineEndRange = NSMakeRange(lineRange.location + lineRange.length - 1, 1) @@ -850,6 +1013,11 @@ public final class TextFieldComponent: Component { } func update(component: TextFieldComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } + self.component = component self.state = state @@ -877,8 +1045,13 @@ public final class TextFieldComponent: Component { let wasEditing = component.externalState.isEditing let isEditing = self.textView.isFirstResponder - if self.textView.textContainerInset != component.insets { - self.textView.textContainerInset = component.insets + var innerTextInsets = component.insets + innerTextInsets.left = 0.0 + + let textLeftInset = component.insets.left + 8.0 + + if self.textView.defaultTextContainerInset != innerTextInsets { + self.textView.defaultTextContainerInset = innerTextInsets } var availableSize = availableSize @@ -886,17 +1059,28 @@ public final class TextFieldComponent: Component { availableSize.width += 32.0 } - self.textContainer.size = CGSize(width: availableSize.width - self.textView.textContainerInset.left - self.textView.textContainerInset.right, height: 10000000.0) - self.layoutManager.ensureLayout(for: self.textContainer) + let textHeight = self.textView.textHeightForWidth(availableSize.width - component.insets.left, rightInset: innerTextInsets.right) + let size = CGSize(width: availableSize.width, height: min(textHeight, availableSize.height)) - let boundingRect = self.layoutManager.boundingRect(forGlyphRange: NSRange(location: 0, length: self.textStorage.length), in: self.textContainer) - let size = CGSize(width: availableSize.width, height: min(availableSize.height, ceil(boundingRect.height) + self.textView.textContainerInset.top + self.textView.textContainerInset.bottom)) + let textFrame = CGRect(origin: CGPoint(x: textLeftInset, y: 0.0), size: CGSize(width: size.width - component.insets.left, height: size.height)) - var refreshScrolling = self.textView.bounds.size != size + var refreshScrolling = self.textView.bounds.size != textFrame.size if component.isOneLineWhenUnfocused && !isEditing && isEditing != wasEditing { refreshScrolling = true } - self.textView.frame = CGRect(origin: CGPoint(), size: size) + + self.textView.theme = ChatInputTextView.Theme( + quote: ChatInputTextView.Theme.Quote( + background: component.textColor.withMultipliedAlpha(0.1), + foreground: component.textColor, + lineStyle: .solid(color: component.textColor), + codeBackground: component.textColor.withMultipliedAlpha(0.1), + codeForeground: component.textColor + ) + ) + + self.textView.frame = textFrame + self.textView.updateLayout(size: textFrame.size) self.textView.panGestureRecognizer.isEnabled = isEditing self.updateEmojiSuggestion(transition: .immediate) @@ -911,9 +1095,9 @@ public final class TextFieldComponent: Component { } } - component.externalState.hasText = self.textStorage.length != 0 + component.externalState.hasText = self.textView.textStorage.length != 0 component.externalState.isEditing = isEditing - component.externalState.textLength = self.textStorage.string.count + component.externalState.textLength = self.textView.textStorage.string.count if let inputView = component.customInputView { if self.textView.inputView == nil { @@ -1011,28 +1195,95 @@ extension TextFieldComponent.InputState { return TextFieldComponent.InputState(inputText: inputText, selectionRange: selectionPosition ..< selectionPosition) } - public func addFormattingAttribute(attribute: NSAttributedString.Key) -> TextFieldComponent.InputState { + public func addFormattingAttribute(attribute: NSAttributedString.Key, value: Any? = nil) -> TextFieldComponent.InputState { if !self.selectionRange.isEmpty { let nsRange = NSRange(location: self.selectionRange.lowerBound, length: self.selectionRange.count) var addAttribute = true var attributesToRemove: [NSAttributedString.Key] = [] - self.inputText.enumerateAttributes(in: nsRange, options: .longestEffectiveRangeNotRequired) { attributes, range, stop in + self.inputText.enumerateAttributes(in: nsRange, options: .longestEffectiveRangeNotRequired) { attributes, range, _ in for (key, _) in attributes { - if key == attribute && range == nsRange { + if key == attribute { addAttribute = false attributesToRemove.append(key) } } } + var selectionRange = self.selectionRange + let result = NSMutableAttributedString(attributedString: self.inputText) for attribute in attributesToRemove { - result.removeAttribute(attribute, range: nsRange) + if attribute == ChatTextInputAttributes.block { + var removeRange = nsRange + + var selectionIndex = nsRange.upperBound + if nsRange.upperBound != result.length && (result.string as NSString).character(at: nsRange.upperBound) != 0x0a { + result.insert(NSAttributedString(string: "\n"), at: nsRange.upperBound) + selectionIndex += 1 + removeRange.length += 1 + } + if nsRange.lowerBound != 0 && (result.string as NSString).character(at: nsRange.lowerBound - 1) != 0x0a { + result.insert(NSAttributedString(string: "\n"), at: nsRange.lowerBound) + selectionIndex += 1 + removeRange.location += 1 + } else if nsRange.lowerBound != 0 { + removeRange.location -= 1 + removeRange.length += 1 + } + + if removeRange.lowerBound > result.length { + removeRange = NSRange(location: result.length, length: 0) + } else if removeRange.upperBound > result.length { + removeRange = NSRange(location: removeRange.lowerBound, length: result.length - removeRange.lowerBound) + } + result.removeAttribute(attribute, range: removeRange) + + if selectionRange.lowerBound > result.length { + selectionRange = result.length ..< result.length + } else if selectionRange.upperBound > result.length { + selectionRange = selectionRange.lowerBound ..< result.length + } + + // Prevent merge back + result.enumerateAttributes(in: NSRange(location: selectionIndex, length: result.length - selectionIndex), options: .longestEffectiveRangeNotRequired) { attributes, range, _ in + for (key, value) in attributes { + if let value = value as? ChatTextInputTextQuoteAttribute { + result.removeAttribute(key, range: range) + result.addAttribute(key, value: ChatTextInputTextQuoteAttribute(kind: value.kind), range: range) + } + } + } + + selectionRange = selectionIndex ..< selectionIndex + } else { + result.removeAttribute(attribute, range: nsRange) + } } + if addAttribute { - result.addAttribute(attribute, value: true as Bool, range: nsRange) + if attribute == ChatTextInputAttributes.block { + result.addAttribute(attribute, value: value ?? ChatTextInputTextQuoteAttribute(kind: .quote), range: nsRange) + var selectionIndex = nsRange.upperBound + if nsRange.upperBound != result.length && (result.string as NSString).character(at: nsRange.upperBound) != 0x0a { + result.insert(NSAttributedString(string: "\n"), at: nsRange.upperBound) + selectionIndex += 1 + } + if nsRange.lowerBound != 0 && (result.string as NSString).character(at: nsRange.lowerBound - 1) != 0x0a { + result.insert(NSAttributedString(string: "\n"), at: nsRange.lowerBound) + selectionIndex += 1 + } + selectionRange = selectionIndex ..< selectionIndex + } else { + result.addAttribute(attribute, value: true as Bool, range: nsRange) + } } - return TextFieldComponent.InputState(inputText: result, selectionRange: self.selectionRange) + if selectionRange.lowerBound > result.length { + selectionRange = result.length ..< result.length + } else if selectionRange.upperBound > result.length { + selectionRange = selectionRange.lowerBound ..< result.length + } + + return TextFieldComponent.InputState(inputText: result, selectionRange: selectionRange) } else { return self } diff --git a/submodules/TelegramUI/Components/VolumeSliderContextItem/Sources/VolumeSliderContextItem.swift b/submodules/TelegramUI/Components/VolumeSliderContextItem/Sources/VolumeSliderContextItem.swift index ad23105f2cd..a1c9465026b 100644 --- a/submodules/TelegramUI/Components/VolumeSliderContextItem/Sources/VolumeSliderContextItem.swift +++ b/submodules/TelegramUI/Components/VolumeSliderContextItem/Sources/VolumeSliderContextItem.swift @@ -10,17 +10,19 @@ import AnimatedCountLabelNode public final class VolumeSliderContextItem: ContextMenuCustomItem { private let minValue: CGFloat + private let maxValue: CGFloat private let value: CGFloat private let valueChanged: (CGFloat, Bool) -> Void - public init(minValue: CGFloat, value: CGFloat, valueChanged: @escaping (CGFloat, Bool) -> Void) { + public init(minValue: CGFloat, maxValue: CGFloat = 1.0, value: CGFloat, valueChanged: @escaping (CGFloat, Bool) -> Void) { self.minValue = minValue + self.maxValue = maxValue self.value = value self.valueChanged = valueChanged } public func node(presentationData: PresentationData, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) -> ContextMenuCustomNode { - return VolumeSliderContextItemNode(presentationData: presentationData, getController: getController, minValue: self.minValue, value: self.value, valueChanged: self.valueChanged) + return VolumeSliderContextItemNode(presentationData: presentationData, getController: getController, minValue: self.minValue, maxValue: self.maxValue, value: self.value, valueChanged: self.valueChanged) } } @@ -40,6 +42,7 @@ private final class VolumeSliderContextItemNode: ASDisplayNode, ContextMenuCusto private let foregroundTextNode: ImmediateAnimatedCountLabelNode let minValue: CGFloat + let maxValue: CGFloat var value: CGFloat = 1.0 { didSet { self.updateValue(transition: .animated(duration: 0.2, curve: .spring)) @@ -50,9 +53,10 @@ private final class VolumeSliderContextItemNode: ASDisplayNode, ContextMenuCusto private let hapticFeedback = HapticFeedback() - init(presentationData: PresentationData, getController: @escaping () -> ContextControllerProtocol?, minValue: CGFloat, value: CGFloat, valueChanged: @escaping (CGFloat, Bool) -> Void) { + init(presentationData: PresentationData, getController: @escaping () -> ContextControllerProtocol?, minValue: CGFloat, maxValue: CGFloat, value: CGFloat, valueChanged: @escaping (CGFloat, Bool) -> Void) { self.presentationData = presentationData self.minValue = minValue + self.maxValue = maxValue self.value = value self.valueChanged = valueChanged @@ -153,8 +157,9 @@ private final class VolumeSliderContextItemNode: ASDisplayNode, ContextMenuCusto private func updateValue(transition: ContainedViewLayoutTransition = .immediate) { let width = self.frame.width + let range = self.maxValue - self.minValue let value = self.value - transition.updateFrameAdditive(node: self.foregroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: value * width, height: self.frame.height))) + transition.updateFrameAdditive(node: self.foregroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: value / range * width, height: self.frame.height))) let stringValue = "\(Int(self.value * 100.0))%" @@ -236,10 +241,10 @@ private final class VolumeSliderContextItemNode: ASDisplayNode, ContextMenuCusto let translation: CGFloat = gestureRecognizer.translation(in: gestureRecognizer.view).x let delta = translation / self.bounds.width - self.value = max(self.minValue, min(1.0, self.value + delta)) + self.value = max(self.minValue, min(self.maxValue, self.value + delta)) gestureRecognizer.setTranslation(CGPoint(), in: gestureRecognizer.view) - if self.value == 1.0 && previousValue != 1.0 { + if self.value == self.maxValue && previousValue != self.maxValue { self.backgroundIconNode.layer.animateScale(from: 1.0, to: 1.1, duration: 0.16, removeOnCompletion: false, completion: { [weak self] _ in if let strongSelf = self { strongSelf.backgroundIconNode.layer.animateScale(from: 1.1, to: 1.0, duration: 0.16) @@ -251,6 +256,8 @@ private final class VolumeSliderContextItemNode: ASDisplayNode, ContextMenuCusto } }) self.hapticFeedback.impact(.soft) + } else if self.maxValue != 1.0 && self.value == 1.0 && previousValue != 1.0 { + self.hapticFeedback.impact(.soft) } else if self.value == 0.0 && previousValue != 0.0 { self.hapticFeedback.impact(.soft) } @@ -260,7 +267,7 @@ private final class VolumeSliderContextItemNode: ASDisplayNode, ContextMenuCusto case .ended: let translation: CGFloat = gestureRecognizer.translation(in: gestureRecognizer.view).x let delta = translation / self.bounds.width - self.value = max(self.minValue, min(1.0, self.value + delta)) + self.value = max(self.minValue, min(self.maxValue, self.value + delta)) self.valueChanged(self.value, true) default: break @@ -269,7 +276,7 @@ private final class VolumeSliderContextItemNode: ASDisplayNode, ContextMenuCusto @objc private func tapGesture(_ gestureRecognizer: UITapGestureRecognizer) { let location = gestureRecognizer.location(in: gestureRecognizer.view) - self.value = max(self.minValue, min(1.0, location.x / self.bounds.width)) + self.value = max(self.minValue, min(self.maxValue, location.x / self.bounds.width)) self.valueChanged(self.value, true) } diff --git a/submodules/TelegramUI/Images.xcassets/Avatar/RepostStoryIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Avatar/RepostStoryIcon.imageset/Contents.json new file mode 100644 index 00000000000..2afe2d007b4 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Avatar/RepostStoryIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "repost_40.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Avatar/RepostStoryIcon.imageset/repost_40.pdf b/submodules/TelegramUI/Images.xcassets/Avatar/RepostStoryIcon.imageset/repost_40.pdf new file mode 100644 index 00000000000..53721d0e241 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Avatar/RepostStoryIcon.imageset/repost_40.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chart/Contents.json b/submodules/TelegramUI/Images.xcassets/Chart/Contents.json index 38f0c81fc22..6e965652df6 100644 --- a/submodules/TelegramUI/Images.xcassets/Chart/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chart/Contents.json @@ -1,9 +1,9 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 }, "properties" : { "provides-namespace" : true } -} \ No newline at end of file +} diff --git a/submodules/TelegramUI/Images.xcassets/Chart/Forwards.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chart/Forwards.imageset/Contents.json new file mode 100644 index 00000000000..427e0560770 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chart/Forwards.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "arrowshape_18.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chart/Forwards.imageset/arrowshape_18.pdf b/submodules/TelegramUI/Images.xcassets/Chart/Forwards.imageset/arrowshape_18.pdf new file mode 100644 index 00000000000..7b29edbc947 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chart/Forwards.imageset/arrowshape_18.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chart/Reactions.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chart/Reactions.imageset/Contents.json new file mode 100644 index 00000000000..5d62e000603 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chart/Reactions.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "heart_18.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chart/Reactions.imageset/heart_18.pdf b/submodules/TelegramUI/Images.xcassets/Chart/Reactions.imageset/heart_18.pdf new file mode 100644 index 00000000000..7e1b15c187f Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chart/Reactions.imageset/heart_18.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Info/SettingsIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Info/SettingsIcon.imageset/Contents.json new file mode 100644 index 00000000000..c991b6cd920 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Info/SettingsIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "settings_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Info/SettingsIcon.imageset/settings_30.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Info/SettingsIcon.imageset/settings_30.pdf new file mode 100644 index 00000000000..843c03d75b6 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Info/SettingsIcon.imageset/settings_30.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/PanelHeartIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/PanelHeartIcon.imageset/Contents.json new file mode 100644 index 00000000000..64da1787989 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/PanelHeartIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "like_24.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/PanelHeartIcon.imageset/like_24.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/PanelHeartIcon.imageset/like_24.pdf new file mode 100644 index 00000000000..a08b631dc81 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/PanelHeartIcon.imageset/like_24.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/Subscriber.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Message/Subscriber.imageset/Contents.json new file mode 100644 index 00000000000..a6b78fde32e --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Message/Subscriber.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "person_6.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/Subscriber.imageset/person_6.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Message/Subscriber.imageset/person_6.pdf new file mode 100644 index 00000000000..9c664dc7433 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Message/Subscriber.imageset/person_6.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/TranscriptionLocked.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Message/TranscriptionLocked.imageset/Contents.json new file mode 100644 index 00000000000..ec75795a909 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Message/TranscriptionLocked.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "lockedaudiototext.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/TranscriptionLocked.imageset/lockedaudiototext.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Message/TranscriptionLocked.imageset/lockedaudiototext.pdf new file mode 100644 index 00000000000..1fadb33c7b9 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Message/TranscriptionLocked.imageset/lockedaudiototext.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Media Editor/RemoveRecordedVideo.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Media Editor/RemoveRecordedVideo.imageset/Contents.json new file mode 100644 index 00000000000..447a7e6314c --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Media Editor/RemoveRecordedVideo.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "removevideomessage_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Media Editor/RemoveRecordedVideo.imageset/removevideomessage_30.pdf b/submodules/TelegramUI/Images.xcassets/Media Editor/RemoveRecordedVideo.imageset/removevideomessage_30.pdf new file mode 100644 index 00000000000..0ac8dba10c4 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Media Editor/RemoveRecordedVideo.imageset/removevideomessage_30.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Navigation/BackArrow.imageset/BackArrow.svg b/submodules/TelegramUI/Images.xcassets/Navigation/BackArrow.imageset/BackArrow.svg new file mode 100644 index 00000000000..46caf41fd73 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Navigation/BackArrow.imageset/BackArrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/submodules/TelegramUI/Images.xcassets/Navigation/BackArrow.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Navigation/BackArrow.imageset/Contents.json new file mode 100644 index 00000000000..000c70dfaf1 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Navigation/BackArrow.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "BackArrow.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Navigation/Contents.json b/submodules/TelegramUI/Images.xcassets/Navigation/Contents.json new file mode 100644 index 00000000000..6e965652df6 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Navigation/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "provides-namespace" : true + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Perk/Colors.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Perk/Colors.imageset/Contents.json new file mode 100644 index 00000000000..c581600204c --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/Perk/Colors.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "brush_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Perk/Colors.imageset/brush_30.pdf b/submodules/TelegramUI/Images.xcassets/Premium/Perk/Colors.imageset/brush_30.pdf new file mode 100644 index 00000000000..474ad798d89 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Premium/Perk/Colors.imageset/brush_30.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Perk/Wallpapers.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Perk/Wallpapers.imageset/Contents.json new file mode 100644 index 00000000000..3bcc9458d1e --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/Perk/Wallpapers.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "wallpapers_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Perk/Wallpapers.imageset/wallpapers_30.pdf b/submodules/TelegramUI/Images.xcassets/Premium/Perk/Wallpapers.imageset/wallpapers_30.pdf new file mode 100644 index 00000000000..c612692389a Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Premium/Perk/Wallpapers.imageset/wallpapers_30.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Stories/Context Menu/Repost.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Stories/Context Menu/Repost.imageset/Contents.json new file mode 100644 index 00000000000..70fb21c3d71 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Stories/Context Menu/Repost.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "repost_24.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Stories/Context Menu/Repost.imageset/repost_24.pdf b/submodules/TelegramUI/Images.xcassets/Stories/Context Menu/Repost.imageset/repost_24.pdf new file mode 100644 index 00000000000..bc733824942 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Stories/Context Menu/Repost.imageset/repost_24.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Stories/HeaderRepost.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Stories/HeaderRepost.imageset/Contents.json new file mode 100644 index 00000000000..a2979707d68 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Stories/HeaderRepost.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "repost_16.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Stories/HeaderRepost.imageset/repost_16.pdf b/submodules/TelegramUI/Images.xcassets/Stories/HeaderRepost.imageset/repost_16.pdf new file mode 100644 index 00000000000..9475ed8afa5 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Stories/HeaderRepost.imageset/repost_16.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Stories/InputRepost.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Stories/InputRepost.imageset/Contents.json new file mode 100644 index 00000000000..62d79d15bff --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Stories/InputRepost.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "repost_30 (2).pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Stories/InputRepost.imageset/repost_30 (2).pdf b/submodules/TelegramUI/Images.xcassets/Stories/InputRepost.imageset/repost_30 (2).pdf new file mode 100644 index 00000000000..622b9494434 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Stories/InputRepost.imageset/repost_30 (2).pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Stories/RepostChannel.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Stories/RepostChannel.imageset/Contents.json new file mode 100644 index 00000000000..fda2c96cbe1 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Stories/RepostChannel.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "channelreply_10.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Stories/RepostChannel.imageset/channelreply_10.pdf b/submodules/TelegramUI/Images.xcassets/Stories/RepostChannel.imageset/channelreply_10.pdf new file mode 100644 index 00000000000..2c9f4a7829d Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Stories/RepostChannel.imageset/channelreply_10.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Stories/RepostUser.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Stories/RepostUser.imageset/Contents.json new file mode 100644 index 00000000000..ed87646773d --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Stories/RepostUser.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "userreply_10.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Stories/RepostUser.imageset/userreply_10.pdf b/submodules/TelegramUI/Images.xcassets/Stories/RepostUser.imageset/userreply_10.pdf new file mode 100644 index 00000000000..57f68fcfb51 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Stories/RepostUser.imageset/userreply_10.pdf differ diff --git a/submodules/TelegramUI/Resources/Animations/BoostReplace.tgs b/submodules/TelegramUI/Resources/Animations/BoostReplace.tgs new file mode 100644 index 00000000000..6524877a994 Binary files /dev/null and b/submodules/TelegramUI/Resources/Animations/BoostReplace.tgs differ diff --git a/submodules/TelegramUI/Resources/Animations/PremiumStar.tgs b/submodules/TelegramUI/Resources/Animations/PremiumStar.tgs new file mode 100644 index 00000000000..1e155d7cf76 Binary files /dev/null and b/submodules/TelegramUI/Resources/Animations/PremiumStar.tgs differ diff --git a/submodules/TelegramUI/Resources/Animations/anim_storyrepost.json b/submodules/TelegramUI/Resources/Animations/anim_storyrepost.json new file mode 100644 index 00000000000..4baa3e947b2 --- /dev/null +++ b/submodules/TelegramUI/Resources/Animations/anim_storyrepost.json @@ -0,0 +1 @@ +{"v":"5.12.1","fr":60,"ip":0,"op":60,"w":512,"h":512,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Arrows","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":1,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":2,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":3,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":4,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":5,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":6,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":7,"s":[0]},{"i":{"x":[0.833],"y":[0.86]},"o":{"x":[0.167],"y":[0]},"t":8,"s":[0]},{"i":{"x":[0.833],"y":[0.346]},"o":{"x":[0.167],"y":[0.083]},"t":9,"s":[0]},{"i":{"x":[0.833],"y":[0.854]},"o":{"x":[0.167],"y":[0.096]},"t":10,"s":[1.683]},{"i":{"x":[0.833],"y":[0.717]},"o":{"x":[0.167],"y":[0.194]},"t":11,"s":[13.202]},{"i":{"x":[0.833],"y":[0.825]},"o":{"x":[0.167],"y":[0.118]},"t":12,"s":[21.881]},{"i":{"x":[0.833],"y":[0.875]},"o":{"x":[0.167],"y":[0.159]},"t":13,"s":[42.689]},{"i":{"x":[0.833],"y":[0.756]},"o":{"x":[0.167],"y":[0.249]},"t":14,"s":[65.632]},{"i":{"x":[0.833],"y":[0.841]},"o":{"x":[0.167],"y":[0.127]},"t":15,"s":[77.183]},{"i":{"x":[0.833],"y":[0.879]},"o":{"x":[0.167],"y":[0.175]},"t":16,"s":[99.424]},{"i":{"x":[0.833],"y":[0.77]},"o":{"x":[0.167],"y":[0.268]},"t":17,"s":[119.61]},{"i":{"x":[0.833],"y":[0.849]},"o":{"x":[0.167],"y":[0.131]},"t":18,"s":[128.714]},{"i":{"x":[0.833],"y":[0.882]},"o":{"x":[0.167],"y":[0.186]},"t":19,"s":[144.735]},{"i":{"x":[0.833],"y":[0.781]},"o":{"x":[0.167],"y":[0.283]},"t":20,"s":[157.784]},{"i":{"x":[0.833],"y":[0.856]},"o":{"x":[0.167],"y":[0.134]},"t":21,"s":[163.227]},{"i":{"x":[0.833],"y":[0.885]},"o":{"x":[0.167],"y":[0.198]},"t":22,"s":[172.101]},{"i":{"x":[0.833],"y":[0.796]},"o":{"x":[0.167],"y":[0.304]},"t":23,"s":[178.567]},{"i":{"x":[0.833],"y":[0.868]},"o":{"x":[0.167],"y":[0.141]},"t":24,"s":[181.011]},{"i":{"x":[0.833],"y":[0.893]},"o":{"x":[0.167],"y":[0.225]},"t":25,"s":[184.564]},{"i":{"x":[0.833],"y":[0.845]},"o":{"x":[0.167],"y":[0.372]},"t":26,"s":[186.647]},{"i":{"x":[0.833],"y":[0.943]},"o":{"x":[0.167],"y":[0.18]},"t":27,"s":[187.247]},{"i":{"x":[0.833],"y":[0.782]},"o":{"x":[0.167],"y":[-0.184]},"t":28,"s":[187.765]},{"i":{"x":[0.833],"y":[0.675]},"o":{"x":[0.167],"y":[0.135]},"t":29,"s":[187.603]},{"i":{"x":[0.833],"y":[0.813]},"o":{"x":[0.167],"y":[0.112]},"t":30,"s":[187.343]},{"i":{"x":[0.833],"y":[0.872]},"o":{"x":[0.167],"y":[0.15]},"t":31,"s":[186.587]},{"i":{"x":[0.833],"y":[0.75]},"o":{"x":[0.167],"y":[0.24]},"t":32,"s":[185.649]},{"i":{"x":[0.833],"y":[0.838]},"o":{"x":[0.167],"y":[0.125]},"t":33,"s":[185.151]},{"i":{"x":[0.833],"y":[0.878]},"o":{"x":[0.167],"y":[0.172]},"t":34,"s":[184.156]},{"i":{"x":[0.833],"y":[0.767]},"o":{"x":[0.167],"y":[0.264]},"t":35,"s":[183.216]},{"i":{"x":[0.833],"y":[0.847]},"o":{"x":[0.167],"y":[0.13]},"t":36,"s":[182.782]},{"i":{"x":[0.833],"y":[0.881]},"o":{"x":[0.167],"y":[0.183]},"t":37,"s":[182.001]},{"i":{"x":[0.833],"y":[0.778]},"o":{"x":[0.167],"y":[0.279]},"t":38,"s":[181.346]},{"i":{"x":[0.833],"y":[0.854]},"o":{"x":[0.167],"y":[0.133]},"t":39,"s":[181.067]},{"i":{"x":[0.833],"y":[0.884]},"o":{"x":[0.167],"y":[0.194]},"t":40,"s":[180.601]},{"i":{"x":[0.833],"y":[0.79]},"o":{"x":[0.167],"y":[0.296]},"t":41,"s":[180.25]},{"i":{"x":[0.833],"y":[0.863]},"o":{"x":[0.167],"y":[0.138]},"t":42,"s":[180.112]},{"i":{"x":[0.833],"y":[0.889]},"o":{"x":[0.167],"y":[0.214]},"t":43,"s":[179.904]},{"i":{"x":[0.833],"y":[0.82]},"o":{"x":[0.167],"y":[0.338]},"t":44,"s":[179.771]},{"i":{"x":[0.833],"y":[0.896]},"o":{"x":[0.167],"y":[0.155]},"t":45,"s":[179.727]},{"i":{"x":[0.833],"y":[0.944]},"o":{"x":[0.167],"y":[0.416]},"t":46,"s":[179.677]},{"i":{"x":[0.833],"y":[0.456]},"o":{"x":[0.167],"y":[-0.17]},"t":47,"s":[179.664]},{"i":{"x":[0.833],"y":[0.787]},"o":{"x":[0.167],"y":[0.098]},"t":48,"s":[179.668]},{"i":{"x":[0.833],"y":[0.869]},"o":{"x":[0.167],"y":[0.137]},"t":49,"s":[179.691]},{"i":{"x":[0.833],"y":[0.742]},"o":{"x":[0.167],"y":[0.228]},"t":50,"s":[179.727]},{"i":{"x":[0.833],"y":[0.834]},"o":{"x":[0.167],"y":[0.123]},"t":51,"s":[179.747]},{"i":{"x":[0.833],"y":[0.877]},"o":{"x":[0.167],"y":[0.167]},"t":52,"s":[179.79]},{"i":{"x":[0.833],"y":[0.763]},"o":{"x":[0.167],"y":[0.258]},"t":53,"s":[179.833]},{"i":{"x":[0.833],"y":[0.845]},"o":{"x":[0.167],"y":[0.129]},"t":54,"s":[179.853]},{"i":{"x":[0.833],"y":[0.88]},"o":{"x":[0.167],"y":[0.18]},"t":55,"s":[179.89]},{"i":{"x":[0.833],"y":[0.775]},"o":{"x":[0.167],"y":[0.275]},"t":56,"s":[179.922]},{"i":{"x":[0.833],"y":[0.852]},"o":{"x":[0.167],"y":[0.132]},"t":57,"s":[179.936]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.19]},"t":58,"s":[179.96]},{"t":59,"s":[179.979]}],"ix":10},"p":{"a":0,"k":[256,256,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[853.333,853.333,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[2.75,-5.25],[-2.75,0],[2.75,5.25]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.67,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.6,"y":0},"t":10,"s":[26.75,39.33],"to":[0,0.333],"ti":[0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0.167},"t":16,"s":[26.75,41.33],"to":[0,0],"ti":[0,0.333]},{"t":24,"s":[26.75,39.33]}],"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},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-2.75,-5.25],[2.75,0],[-2.75,5.25]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.67,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.6,"y":0},"t":10,"s":[33.25,20.67],"to":[0,-0.333],"ti":[0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0.167},"t":16,"s":[33.25,18.67],"to":[0,0],"ti":[0,-0.333]},{"t":24,"s":[33.25,20.67]}],"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 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"aArtboard Copy Outlines 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256,256,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[853.333,853.333,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.761,0],[0,0],[0,-2.761],[0,0],[2.761,0],[0,0],[0,2.761],[0,0]],"o":[[0,0],[2.761,0],[0,0],[0,2.761],[0,0],[-2.761,0],[0,0],[0,-2.761]],"v":[[-6,-9.33],[6,-9.33],[11,-4.33],[11,4.33],[6,9.33],[-6,9.33],[-11,4.33],[-11,-4.33]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.67,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":63.25,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[232]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":1,"s":[232]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":2,"s":[232]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":3,"s":[232]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":4,"s":[232]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":5,"s":[232]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":6,"s":[232]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":7,"s":[232]},{"i":{"x":[0.833],"y":[0.86]},"o":{"x":[0.167],"y":[0]},"t":8,"s":[232]},{"i":{"x":[0.833],"y":[0.346]},"o":{"x":[0.167],"y":[0.083]},"t":9,"s":[232]},{"i":{"x":[0.833],"y":[0.854]},"o":{"x":[0.167],"y":[0.096]},"t":10,"s":[233.683]},{"i":{"x":[0.833],"y":[0.717]},"o":{"x":[0.167],"y":[0.194]},"t":11,"s":[245.202]},{"i":{"x":[0.833],"y":[0.825]},"o":{"x":[0.167],"y":[0.118]},"t":12,"s":[253.881]},{"i":{"x":[0.833],"y":[0.875]},"o":{"x":[0.167],"y":[0.159]},"t":13,"s":[274.689]},{"i":{"x":[0.833],"y":[0.756]},"o":{"x":[0.167],"y":[0.249]},"t":14,"s":[297.632]},{"i":{"x":[0.833],"y":[0.841]},"o":{"x":[0.167],"y":[0.127]},"t":15,"s":[309.183]},{"i":{"x":[0.833],"y":[0.879]},"o":{"x":[0.167],"y":[0.175]},"t":16,"s":[331.424]},{"i":{"x":[0.833],"y":[0.77]},"o":{"x":[0.167],"y":[0.268]},"t":17,"s":[351.61]},{"i":{"x":[0.833],"y":[0.849]},"o":{"x":[0.167],"y":[0.131]},"t":18,"s":[360.714]},{"i":{"x":[0.833],"y":[0.882]},"o":{"x":[0.167],"y":[0.186]},"t":19,"s":[376.735]},{"i":{"x":[0.833],"y":[0.781]},"o":{"x":[0.167],"y":[0.283]},"t":20,"s":[389.784]},{"i":{"x":[0.833],"y":[0.856]},"o":{"x":[0.167],"y":[0.134]},"t":21,"s":[395.227]},{"i":{"x":[0.833],"y":[0.885]},"o":{"x":[0.167],"y":[0.198]},"t":22,"s":[404.101]},{"i":{"x":[0.833],"y":[0.796]},"o":{"x":[0.167],"y":[0.304]},"t":23,"s":[410.567]},{"i":{"x":[0.833],"y":[0.868]},"o":{"x":[0.167],"y":[0.141]},"t":24,"s":[413.011]},{"i":{"x":[0.833],"y":[0.893]},"o":{"x":[0.167],"y":[0.225]},"t":25,"s":[416.564]},{"i":{"x":[0.833],"y":[0.845]},"o":{"x":[0.167],"y":[0.372]},"t":26,"s":[418.647]},{"i":{"x":[0.833],"y":[0.943]},"o":{"x":[0.167],"y":[0.18]},"t":27,"s":[419.247]},{"i":{"x":[0.833],"y":[0.782]},"o":{"x":[0.167],"y":[-0.184]},"t":28,"s":[419.765]},{"i":{"x":[0.833],"y":[0.675]},"o":{"x":[0.167],"y":[0.135]},"t":29,"s":[419.603]},{"i":{"x":[0.833],"y":[0.813]},"o":{"x":[0.167],"y":[0.112]},"t":30,"s":[419.343]},{"i":{"x":[0.833],"y":[0.872]},"o":{"x":[0.167],"y":[0.15]},"t":31,"s":[418.587]},{"i":{"x":[0.833],"y":[0.75]},"o":{"x":[0.167],"y":[0.24]},"t":32,"s":[417.649]},{"i":{"x":[0.833],"y":[0.838]},"o":{"x":[0.167],"y":[0.125]},"t":33,"s":[417.151]},{"i":{"x":[0.833],"y":[0.878]},"o":{"x":[0.167],"y":[0.172]},"t":34,"s":[416.156]},{"i":{"x":[0.833],"y":[0.767]},"o":{"x":[0.167],"y":[0.264]},"t":35,"s":[415.216]},{"i":{"x":[0.833],"y":[0.847]},"o":{"x":[0.167],"y":[0.13]},"t":36,"s":[414.782]},{"i":{"x":[0.833],"y":[0.881]},"o":{"x":[0.167],"y":[0.183]},"t":37,"s":[414.001]},{"i":{"x":[0.833],"y":[0.778]},"o":{"x":[0.167],"y":[0.279]},"t":38,"s":[413.346]},{"i":{"x":[0.833],"y":[0.854]},"o":{"x":[0.167],"y":[0.133]},"t":39,"s":[413.067]},{"i":{"x":[0.833],"y":[0.884]},"o":{"x":[0.167],"y":[0.194]},"t":40,"s":[412.601]},{"i":{"x":[0.833],"y":[0.79]},"o":{"x":[0.167],"y":[0.296]},"t":41,"s":[412.25]},{"i":{"x":[0.833],"y":[0.863]},"o":{"x":[0.167],"y":[0.138]},"t":42,"s":[412.112]},{"i":{"x":[0.833],"y":[0.889]},"o":{"x":[0.167],"y":[0.214]},"t":43,"s":[411.904]},{"i":{"x":[0.833],"y":[0.82]},"o":{"x":[0.167],"y":[0.338]},"t":44,"s":[411.771]},{"i":{"x":[0.833],"y":[0.896]},"o":{"x":[0.167],"y":[0.155]},"t":45,"s":[411.727]},{"i":{"x":[0.833],"y":[0.944]},"o":{"x":[0.167],"y":[0.416]},"t":46,"s":[411.677]},{"i":{"x":[0.833],"y":[0.456]},"o":{"x":[0.167],"y":[-0.17]},"t":47,"s":[411.664]},{"i":{"x":[0.833],"y":[0.787]},"o":{"x":[0.167],"y":[0.098]},"t":48,"s":[411.668]},{"i":{"x":[0.833],"y":[0.869]},"o":{"x":[0.167],"y":[0.137]},"t":49,"s":[411.691]},{"i":{"x":[0.833],"y":[0.742]},"o":{"x":[0.167],"y":[0.228]},"t":50,"s":[411.727]},{"i":{"x":[0.833],"y":[0.834]},"o":{"x":[0.167],"y":[0.123]},"t":51,"s":[411.747]},{"i":{"x":[0.833],"y":[0.877]},"o":{"x":[0.167],"y":[0.167]},"t":52,"s":[411.79]},{"i":{"x":[0.833],"y":[0.763]},"o":{"x":[0.167],"y":[0.258]},"t":53,"s":[411.833]},{"i":{"x":[0.833],"y":[0.845]},"o":{"x":[0.167],"y":[0.129]},"t":54,"s":[411.853]},{"i":{"x":[0.833],"y":[0.88]},"o":{"x":[0.167],"y":[0.18]},"t":55,"s":[411.89]},{"i":{"x":[0.833],"y":[0.775]},"o":{"x":[0.167],"y":[0.275]},"t":56,"s":[411.922]},{"i":{"x":[0.833],"y":[0.852]},"o":{"x":[0.167],"y":[0.132]},"t":57,"s":[411.936]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.19]},"t":58,"s":[411.96]},{"t":59,"s":[411.979]}],"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[30,30],"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 6","np":3,"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":[[-2.761,0],[0,0],[0,-2.761],[0,0],[2.761,0],[0,0],[0,2.761],[0,0]],"o":[[0,0],[2.761,0],[0,0],[0,2.761],[0,0],[-2.761,0],[0,0],[0,-2.761]],"v":[[-6,-9.33],[6,-9.33],[11,-4.33],[11,4.33],[6,9.33],[-6,9.33],[-11,4.33],[-11,-4.33]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":63.25,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[52]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":1,"s":[52]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":2,"s":[52]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":3,"s":[52]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":4,"s":[52]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":5,"s":[52]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":6,"s":[52]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":7,"s":[52]},{"i":{"x":[0.833],"y":[0.86]},"o":{"x":[0.167],"y":[0]},"t":8,"s":[52]},{"i":{"x":[0.833],"y":[0.346]},"o":{"x":[0.167],"y":[0.083]},"t":9,"s":[52]},{"i":{"x":[0.833],"y":[0.854]},"o":{"x":[0.167],"y":[0.096]},"t":10,"s":[53.683]},{"i":{"x":[0.833],"y":[0.717]},"o":{"x":[0.167],"y":[0.194]},"t":11,"s":[65.202]},{"i":{"x":[0.833],"y":[0.825]},"o":{"x":[0.167],"y":[0.118]},"t":12,"s":[73.881]},{"i":{"x":[0.833],"y":[0.875]},"o":{"x":[0.167],"y":[0.159]},"t":13,"s":[94.689]},{"i":{"x":[0.833],"y":[0.756]},"o":{"x":[0.167],"y":[0.249]},"t":14,"s":[117.632]},{"i":{"x":[0.833],"y":[0.841]},"o":{"x":[0.167],"y":[0.127]},"t":15,"s":[129.183]},{"i":{"x":[0.833],"y":[0.879]},"o":{"x":[0.167],"y":[0.175]},"t":16,"s":[151.424]},{"i":{"x":[0.833],"y":[0.77]},"o":{"x":[0.167],"y":[0.268]},"t":17,"s":[171.61]},{"i":{"x":[0.833],"y":[0.849]},"o":{"x":[0.167],"y":[0.131]},"t":18,"s":[180.714]},{"i":{"x":[0.833],"y":[0.882]},"o":{"x":[0.167],"y":[0.186]},"t":19,"s":[196.735]},{"i":{"x":[0.833],"y":[0.781]},"o":{"x":[0.167],"y":[0.283]},"t":20,"s":[209.784]},{"i":{"x":[0.833],"y":[0.856]},"o":{"x":[0.167],"y":[0.134]},"t":21,"s":[215.227]},{"i":{"x":[0.833],"y":[0.885]},"o":{"x":[0.167],"y":[0.198]},"t":22,"s":[224.101]},{"i":{"x":[0.833],"y":[0.796]},"o":{"x":[0.167],"y":[0.304]},"t":23,"s":[230.567]},{"i":{"x":[0.833],"y":[0.868]},"o":{"x":[0.167],"y":[0.141]},"t":24,"s":[233.011]},{"i":{"x":[0.833],"y":[0.893]},"o":{"x":[0.167],"y":[0.225]},"t":25,"s":[236.564]},{"i":{"x":[0.833],"y":[0.845]},"o":{"x":[0.167],"y":[0.372]},"t":26,"s":[238.647]},{"i":{"x":[0.833],"y":[0.943]},"o":{"x":[0.167],"y":[0.18]},"t":27,"s":[239.247]},{"i":{"x":[0.833],"y":[0.782]},"o":{"x":[0.167],"y":[-0.184]},"t":28,"s":[239.765]},{"i":{"x":[0.833],"y":[0.675]},"o":{"x":[0.167],"y":[0.135]},"t":29,"s":[239.603]},{"i":{"x":[0.833],"y":[0.813]},"o":{"x":[0.167],"y":[0.112]},"t":30,"s":[239.343]},{"i":{"x":[0.833],"y":[0.872]},"o":{"x":[0.167],"y":[0.15]},"t":31,"s":[238.587]},{"i":{"x":[0.833],"y":[0.75]},"o":{"x":[0.167],"y":[0.24]},"t":32,"s":[237.649]},{"i":{"x":[0.833],"y":[0.838]},"o":{"x":[0.167],"y":[0.125]},"t":33,"s":[237.151]},{"i":{"x":[0.833],"y":[0.878]},"o":{"x":[0.167],"y":[0.172]},"t":34,"s":[236.156]},{"i":{"x":[0.833],"y":[0.767]},"o":{"x":[0.167],"y":[0.264]},"t":35,"s":[235.216]},{"i":{"x":[0.833],"y":[0.847]},"o":{"x":[0.167],"y":[0.13]},"t":36,"s":[234.782]},{"i":{"x":[0.833],"y":[0.881]},"o":{"x":[0.167],"y":[0.183]},"t":37,"s":[234.001]},{"i":{"x":[0.833],"y":[0.778]},"o":{"x":[0.167],"y":[0.279]},"t":38,"s":[233.346]},{"i":{"x":[0.833],"y":[0.854]},"o":{"x":[0.167],"y":[0.133]},"t":39,"s":[233.067]},{"i":{"x":[0.833],"y":[0.884]},"o":{"x":[0.167],"y":[0.194]},"t":40,"s":[232.601]},{"i":{"x":[0.833],"y":[0.79]},"o":{"x":[0.167],"y":[0.296]},"t":41,"s":[232.25]},{"i":{"x":[0.833],"y":[0.863]},"o":{"x":[0.167],"y":[0.138]},"t":42,"s":[232.112]},{"i":{"x":[0.833],"y":[0.889]},"o":{"x":[0.167],"y":[0.214]},"t":43,"s":[231.904]},{"i":{"x":[0.833],"y":[0.82]},"o":{"x":[0.167],"y":[0.338]},"t":44,"s":[231.771]},{"i":{"x":[0.833],"y":[0.896]},"o":{"x":[0.167],"y":[0.155]},"t":45,"s":[231.727]},{"i":{"x":[0.833],"y":[0.944]},"o":{"x":[0.167],"y":[0.416]},"t":46,"s":[231.677]},{"i":{"x":[0.833],"y":[0.456]},"o":{"x":[0.167],"y":[-0.17]},"t":47,"s":[231.664]},{"i":{"x":[0.833],"y":[0.787]},"o":{"x":[0.167],"y":[0.098]},"t":48,"s":[231.668]},{"i":{"x":[0.833],"y":[0.869]},"o":{"x":[0.167],"y":[0.137]},"t":49,"s":[231.691]},{"i":{"x":[0.833],"y":[0.742]},"o":{"x":[0.167],"y":[0.228]},"t":50,"s":[231.727]},{"i":{"x":[0.833],"y":[0.834]},"o":{"x":[0.167],"y":[0.123]},"t":51,"s":[231.747]},{"i":{"x":[0.833],"y":[0.877]},"o":{"x":[0.167],"y":[0.167]},"t":52,"s":[231.79]},{"i":{"x":[0.833],"y":[0.763]},"o":{"x":[0.167],"y":[0.258]},"t":53,"s":[231.833]},{"i":{"x":[0.833],"y":[0.845]},"o":{"x":[0.167],"y":[0.129]},"t":54,"s":[231.853]},{"i":{"x":[0.833],"y":[0.88]},"o":{"x":[0.167],"y":[0.18]},"t":55,"s":[231.89]},{"i":{"x":[0.833],"y":[0.775]},"o":{"x":[0.167],"y":[0.275]},"t":56,"s":[231.922]},{"i":{"x":[0.833],"y":[0.852]},"o":{"x":[0.167],"y":[0.132]},"t":57,"s":[231.936]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.19]},"t":58,"s":[231.96]},{"t":59,"s":[231.979]}],"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.67,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[30,30],"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 5","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}} \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/AccountContext.swift b/submodules/TelegramUI/Sources/AccountContext.swift index 3fb0af2bbe0..5441f218b8f 100644 --- a/submodules/TelegramUI/Sources/AccountContext.swift +++ b/submodules/TelegramUI/Sources/AccountContext.swift @@ -247,11 +247,14 @@ public final class AccountContextImpl: AccountContext { private var peerNameColorsConfigurationDisposable: Disposable? public private(set) var peerNameColors: PeerNameColors + private var audioTranscriptionTrialDisposable: Disposable? + public private(set) var audioTranscriptionTrial: AudioTranscription.TrialState + public private(set) var isPremium: Bool public let imageCache: AnyObject? - public init(sharedContext: SharedAccountContextImpl, account: Account, limitsConfiguration: LimitsConfiguration, contentSettings: ContentSettings, appConfiguration: AppConfiguration, temp: Bool = false) + public init(sharedContext: SharedAccountContextImpl, account: Account, limitsConfiguration: LimitsConfiguration, contentSettings: ContentSettings, appConfiguration: AppConfiguration, availableReplyColors: EngineAvailableColorOptions, availableProfileColors: EngineAvailableColorOptions, temp: Bool = false) { self.sharedContextImpl = sharedContext self.account = account @@ -260,7 +263,8 @@ public final class AccountContextImpl: AccountContext { self.imageCache = DirectMediaImageCache(account: account) self.userLimits = EngineConfiguration.UserLimits(UserLimitsConfiguration.defaultValue) - self.peerNameColors = PeerNameColors.defaultValue + self.peerNameColors = PeerNameColors.with(availableReplyColors: availableReplyColors, availableProfileColors: availableProfileColors) + self.audioTranscriptionTrial = AudioTranscription.TrialState.defaultValue self.isPremium = false self.downloadedMediaStoreManager = DownloadedMediaStoreManagerImpl(postbox: account.postbox, accountManager: sharedContext.accountManager) @@ -407,12 +411,31 @@ public final class AccountContextImpl: AccountContext { self.userLimits = userLimits }) - self.peerNameColorsConfigurationDisposable = (self._appConfiguration.get() - |> deliverOnMainQueue).startStrict(next: { [weak self] appConfiguration in + self.peerNameColorsConfigurationDisposable = (combineLatest( + self.engine.accountData.observeAvailableColorOptions(scope: .replies), + self.engine.accountData.observeAvailableColorOptions(scope: .profile) + ) + |> deliverOnMainQueue).startStrict(next: { [weak self] availableReplyColors, availableProfileColors in + guard let self = self else { + return + } + self.peerNameColors = PeerNameColors.with(availableReplyColors: availableReplyColors, availableProfileColors: availableProfileColors) + }) + + self.audioTranscriptionTrialDisposable = (self.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: account.peerId)) + |> mapToSignal { peer -> Signal in + let isPremium = peer?.isPremium ?? false + if isPremium { + return .single(AudioTranscription.TrialState(cooldownUntilTime: nil, remainingCount: 1)) + } else { + return self.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.AudioTranscriptionTrial()) + } + } + |> deliverOnMainQueue).startStrict(next: { [weak self] audioTranscriptionTrial in guard let self = self else { return } - self.peerNameColors = PeerNameColors.with(appConfiguration: appConfiguration) + self.audioTranscriptionTrial = audioTranscriptionTrial }) } @@ -703,11 +726,6 @@ private final class ChatLocationReplyContextHolderImpl: ChatLocationContextHolde } } -func getAppConfiguration(transaction: Transaction) -> AppConfiguration { - let appConfiguration: AppConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration)?.get(AppConfiguration.self) ?? AppConfiguration.defaultValue - return appConfiguration -} - func getAppConfiguration(postbox: Postbox) -> Signal { return postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]) |> map { view -> AppConfiguration in diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift index 161fcf20e61..d456a1f0278 100644 --- a/submodules/TelegramUI/Sources/AppDelegate.swift +++ b/submodules/TelegramUI/Sources/AppDelegate.swift @@ -59,6 +59,7 @@ import DeviceProximity import MediaEditor import TelegramUIDeclareEncodables import ContextMenuScreen +import MetalEngine #if canImport(AppCenter) import AppCenter @@ -445,10 +446,13 @@ private class UserInterfaceStyleObserverWindow: UIWindow { self.window = window self.nativeWindow = window + hostView.containerView.layer.addSublayer(MetalEngine.shared.rootLayer) + if !UIDevice.current.isBatteryMonitoringEnabled { UIDevice.current.isBatteryMonitoringEnabled = true } + let clearNotificationsManager = ClearNotificationsManager(getNotificationIds: { completion in if #available(iOS 10.0, *) { UNUserNotificationCenter.current().getDeliveredNotifications(completionHandler: { notifications in @@ -2754,7 +2758,7 @@ private class UserInterfaceStyleObserverWindow: UIWindow { let userInfo = response.notification.request.content.userInfo if let nicegramDeeplink = userInfo["nicegramDeeplink"] as? String, let url = URL(string: nicegramDeeplink) { - UIApplication.shared.open(url) + CoreContainer.shared.urlOpener().open(url) completionHandler() return } diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift new file mode 100644 index 00000000000..014e6ac8dff --- /dev/null +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift @@ -0,0 +1,412 @@ +import Foundation +import UIKit +import Postbox +import TelegramCore +import SwiftSignalKit +import Display +import ChatPresentationInterfaceState +import AccountContext +import ChatControllerInteraction +import OverlayStatusController +import TelegramPresentationData +import PresentationDataUtils + +extension ChatControllerImpl { + func navigateToMessage( + fromId: MessageId, + id: MessageId, + params: NavigateToMessageParams + ) { + var id = id + if case let .replyThread(message) = self.chatLocation { + if let channelMessageId = message.channelMessageId, id == channelMessageId { + id = message.messageId + } + } + + let continueNavigation: () -> Void = { [weak self] in + guard let self else { + return + } + self.navigateToMessage(from: fromId, to: .id(id, params), forceInCurrentChat: fromId.peerId == id.peerId) + } + + let _ = (self.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: id.peerId) + ) + |> deliverOnMainQueue).startStandalone(next: { [weak self] toPeer in + guard let self else { + return + } + + if params.quote != nil { + if let toPeer { + switch toPeer { + case let .channel(channel): + if channel.username == nil && channel.usernames.isEmpty { + switch channel.participationStatus { + case .kicked, .left: + self.controllerInteraction?.attemptedNavigationToPrivateQuote(toPeer._asPeer()) + return + case .member: + break + } + } + default: + break + } + } else { + self.controllerInteraction?.attemptedNavigationToPrivateQuote(nil) + return + } + } + + continueNavigation() + }) + } + + func navigateToMessage( + from fromId: MessageId?, + to messageLocation: NavigateToMessageLocation, + scrollPosition: ListViewScrollPosition = .center(.bottom), + rememberInStack: Bool = true, + forceInCurrentChat: Bool = false, + dropStack: Bool = false, + animated: Bool = true, + completion: (() -> Void)? = nil, + customPresentProgress: ((ViewController, Any?) -> Void)? = nil, + statusSubject: ChatLoadingMessageSubject = .generic + ) { + if !self.isNodeLoaded { + completion?() + return + } + var fromIndex: MessageIndex? + + if let fromId = fromId, let message = self.chatDisplayNode.historyNode.messageInCurrentHistoryView(fromId) { + fromIndex = message.index + } else { + if let message = self.chatDisplayNode.historyNode.anchorMessageInCurrentHistoryView() { + fromIndex = message.index + } + } + + var isScheduledMessages = false + var isPinnedMessages = false + if case .scheduledMessages = self.presentationInterfaceState.subject { + isScheduledMessages = true + } else if case .pinnedMessages = self.presentationInterfaceState.subject { + isPinnedMessages = true + } + + var forceInCurrentChat = forceInCurrentChat + if case let .peer(peerId) = self.chatLocation, messageLocation.peerId == peerId, !isPinnedMessages, !isScheduledMessages { + forceInCurrentChat = true + } + + if isPinnedMessages, let messageId = messageLocation.messageId { + let _ = (combineLatest( + self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: messageId.peerId)), + self.context.engine.messages.getMessagesLoadIfNecessary([messageId], strategy: .local) + |> mapToSignal { result -> Signal<[Message], NoError> in + guard case let .result(result) = result else { + return .complete() + } + return .single(result) + } + ) + |> deliverOnMainQueue).startStandalone(next: { [weak self] peer, messages in + guard let self, let peer = peer else { + return + } + guard let navigationController = self.effectiveNavigationController else { + return + } + + self.dismiss() + + let navigateToLocation: NavigateToChatControllerParams.Location + if let message = messages.first, let threadId = message.threadId, let channel = message.peers[message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum) { + navigateToLocation = .replyThread(ChatReplyThreadMessage(messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) + } else { + navigateToLocation = .peer(peer) + } + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: navigateToLocation, subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil), keepStack: .always)) + }) + } else if case let .peer(peerId) = self.chatLocation, let messageId = messageLocation.messageId, (messageId.peerId != peerId && !forceInCurrentChat) || (isScheduledMessages && messageId.id != 0 && !Namespaces.Message.allScheduled.contains(messageId.namespace)) { + let _ = (self.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: messageId.peerId), + TelegramEngine.EngineData.Item.Messages.Message(id: messageId) + ) + |> deliverOnMainQueue).startStandalone(next: { [weak self] peer, message in + guard let self, let peer = peer else { + return + } + if let navigationController = self.effectiveNavigationController { + var chatLocation: NavigateToChatControllerParams.Location = .peer(peer) + if case let .channel(channel) = peer, channel.flags.contains(.isForum), let message = message, let threadId = message.threadId { + chatLocation = .replyThread(ChatReplyThreadMessage(messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) + } + + var quote: ChatControllerSubject.MessageHighlight.Quote? + if case let .id(_, params) = messageLocation { + quote = params.quote.flatMap { quote in ChatControllerSubject.MessageHighlight.Quote(string: quote.string, offset: quote.offset) } + } + + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: chatLocation, subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: quote), timecode: nil), keepStack: .always)) + } + }) + } else if forceInCurrentChat { + if let _ = fromId, let fromIndex = fromIndex, rememberInStack { + self.historyNavigationStack.add(fromIndex) + } + + let scrollFromIndex: MessageIndex? + if let fromIndex = fromIndex { + scrollFromIndex = fromIndex + } else if let message = self.chatDisplayNode.historyNode.lastVisbleMesssage() { + scrollFromIndex = message.index + } else { + scrollFromIndex = nil + } + + if let scrollFromIndex = scrollFromIndex { + if let messageId = messageLocation.messageId, let message = self.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId) { + self.loadingMessage.set(.single(nil)) + self.messageIndexDisposable.set(nil) + + var delayCompletion = true + if self.chatDisplayNode.historyNode.isMessageVisible(id: messageId) { + delayCompletion = false + } + + var quote: (string: String, offset: Int?)? + if case let .id(_, params) = messageLocation { + quote = params.quote.flatMap { quote in (string: quote.string, offset: quote.offset) } + } + self.chatDisplayNode.historyNode.scrollToMessage(from: scrollFromIndex, to: message.index, animated: animated, quote: quote, scrollPosition: scrollPosition) + + if delayCompletion { + Queue.mainQueue().after(0.25, { + completion?() + }) + } else { + Queue.mainQueue().justDispatch({ + completion?() + }) + } + + if case let .id(_, params) = messageLocation, let timecode = params.timestamp { + let _ = self.controllerInteraction?.openMessage(message, OpenMessageParams(mode: .timecode(timecode))) + } + } else if case let .index(index) = messageLocation, index.id.id == 0, index.timestamp > 0, case .scheduledMessages = self.presentationInterfaceState.subject { + self.chatDisplayNode.historyNode.scrollToMessage(from: scrollFromIndex, to: index, animated: animated, scrollPosition: scrollPosition) + } else { + var quote: (string: String, offset: Int?)? + if case let .id(messageId, params) = messageLocation { + if params.timestamp != nil { + self.scheduledScrollToMessageId = (messageId, params) + } + quote = params.quote.flatMap { ($0.string, $0.offset) } + } + var progress: Promise? + if case let .id(_, params) = messageLocation { + progress = params.progress + } + self.loadingMessage.set(.single(statusSubject) |> delay(0.1, queue: .mainQueue())) + + let searchLocation: ChatHistoryInitialSearchLocation + switch messageLocation { + case let .id(id, _): + if case let .replyThread(message) = self.chatLocation, id == message.messageId { + searchLocation = .index(.absoluteLowerBound()) + } else { + searchLocation = .id(id) + } + case let .index(index): + searchLocation = .index(index) + case .upperBound: + if let peerId = self.chatLocation.peerId { + searchLocation = .index(MessageIndex.upperBound(peerId: peerId)) + } else { + searchLocation = .index(.absoluteUpperBound()) + } + } + var historyView: Signal + historyView = preloadedChatHistoryViewForLocation(ChatHistoryLocationInput(content: .InitialSearch(subject: MessageHistoryInitialSearchSubject(location: searchLocation, quote: nil), count: 50, highlight: true), id: 0), context: self.context, chatLocation: self.chatLocation, subject: self.subject, chatLocationContextHolder: self.chatLocationContextHolder, fixedCombinedReadStates: nil, tagMask: nil, additionalData: []) + + var signal: Signal<(MessageIndex?, Bool), NoError> + signal = historyView + |> mapToSignal { historyView -> Signal<(MessageIndex?, Bool), NoError> in + switch historyView { + case .Loading: + return .single((nil, true)) + case let .HistoryView(view, _, _, _, _, _, _): + for entry in view.entries { + if entry.message.id == messageLocation.messageId { + return .single((entry.message.index, false)) + } + } + if case let .index(index) = searchLocation { + return .single((index, false)) + } + return .single((nil, false)) + } + } + |> take(until: { index in + return SignalTakeAction(passthrough: true, complete: !index.1) + }) + + /*#if DEBUG + signal = .single((nil, true)) |> then(signal |> delay(2.0, queue: .mainQueue())) + #endif*/ + + var cancelImpl: (() -> Void)? + let presentationData = self.presentationData + let displayTime = CACurrentMediaTime() + let progressSignal = Signal { [weak self] subscriber in + if let progress { + progress.set(.single(true)) + return ActionDisposable { + Queue.mainQueue().async() { + progress.set(.single(false)) + } + } + } else if case .generic = statusSubject { + let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { + if CACurrentMediaTime() - displayTime > 1.5 { + cancelImpl?() + } + })) + if let customPresentProgress = customPresentProgress { + customPresentProgress(controller, nil) + } else { + self?.present(controller, in: .window(.root)) + } + return ActionDisposable { [weak controller] in + Queue.mainQueue().async() { + controller?.dismiss() + } + } + } else { + return EmptyDisposable + } + } + |> runOn(Queue.mainQueue()) + |> delay(0.05, queue: Queue.mainQueue()) + let progressDisposable = MetaDisposable() + var progressStarted = false + self.messageIndexDisposable.set((signal + |> afterDisposed { + Queue.mainQueue().async { + progressDisposable.dispose() + } + } + |> deliverOnMainQueue).startStrict(next: { [weak self] index in + if let strongSelf = self, let index = index.0 { + strongSelf.chatDisplayNode.historyNode.scrollToMessage(from: scrollFromIndex, to: index, animated: animated, quote: quote, scrollPosition: scrollPosition) + completion?() + } else if index.1 { + if !progressStarted { + progressStarted = true + progressDisposable.set(progressSignal.start()) + } + } + }, completed: { [weak self] in + if let strongSelf = self { + strongSelf.loadingMessage.set(.single(nil)) + } + })) + cancelImpl = { [weak self] in + if let strongSelf = self { + strongSelf.loadingMessage.set(.single(nil)) + strongSelf.messageIndexDisposable.set(nil) + } + } + } + } else { + completion?() + } + } else { + if let fromIndex = fromIndex { + let searchLocation: ChatHistoryInitialSearchLocation + switch messageLocation { + case let .id(id, _): + searchLocation = .id(id) + case let .index(index): + searchLocation = .index(index) + case .upperBound: + return + } + if let _ = fromId, rememberInStack { + self.historyNavigationStack.add(fromIndex) + } + self.loadingMessage.set(.single(statusSubject) |> delay(0.1, queue: .mainQueue())) + + var quote: ChatControllerSubject.MessageHighlight.Quote? + if case let .id(_, params) = messageLocation { + quote = params.quote.flatMap { quote in ChatControllerSubject.MessageHighlight.Quote(string: quote.string, offset: quote.offset) } + } + + let historyView = preloadedChatHistoryViewForLocation(ChatHistoryLocationInput(content: .InitialSearch(subject: MessageHistoryInitialSearchSubject(location: searchLocation, quote: quote.flatMap { quote in MessageHistoryInitialSearchSubject.Quote(string: quote.string, offset: quote.offset) }), count: 50, highlight: true), id: 0), context: self.context, chatLocation: self.chatLocation, subject: self.subject, chatLocationContextHolder: self.chatLocationContextHolder, fixedCombinedReadStates: nil, tagMask: nil, additionalData: []) + var signal: Signal + signal = historyView + |> mapToSignal { historyView -> Signal in + switch historyView { + case .Loading: + return .complete() + case let .HistoryView(view, _, _, _, _, _, _): + for entry in view.entries { + if entry.message.id == messageLocation.messageId { + return .single(entry.message.index) + } + } + return .single(nil) + } + } + |> take(1) + + self.messageIndexDisposable.set((signal |> deliverOnMainQueue).startStrict(next: { [weak self] index in + if let strongSelf = self { + if let index = index { + strongSelf.chatDisplayNode.historyNode.scrollToMessage(from: fromIndex, to: index, animated: animated, scrollPosition: scrollPosition) + completion?() + } else { + let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: messageLocation.peerId)) + |> deliverOnMainQueue).startStandalone(next: { peer in + guard let strongSelf = self, let peer = peer else { + return + } + + if let navigationController = strongSelf.effectiveNavigationController { + var quote: ChatControllerSubject.MessageHighlight.Quote? + if case let .id(_, params) = messageLocation { + quote = params.quote.flatMap { quote in ChatControllerSubject.MessageHighlight.Quote(string: quote.string, offset: quote.offset) } + } + + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), subject: messageLocation.messageId.flatMap { .message(id: .id($0), highlight: ChatControllerSubject.MessageHighlight(quote: quote), timecode: nil) })) + } + }) + completion?() + } + } + }, completed: { [weak self] in + if let strongSelf = self { + strongSelf.loadingMessage.set(.single(nil)) + } + })) + } else { + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: messageLocation.peerId)) + |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in + guard let self, let peer = peer else { + return + } + if let navigationController = self.effectiveNavigationController { + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), subject: messageLocation.messageId.flatMap { .message(id: .id($0), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil) })) + } + completion?() + }) + } + } + } +} diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift new file mode 100644 index 00000000000..e9707619da6 --- /dev/null +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift @@ -0,0 +1,439 @@ +import Foundation +import UIKit +import SwiftSignalKit +import Postbox +import TelegramCore +import AsyncDisplayKit +import Display +import TelegramNotices +import ContextUI +import AccountContext +import ChatMessageItemView +import ChatMessageItemCommon +import ReactionSelectionNode +import EntityKeyboard +import TextNodeWithEntities +import PremiumUI +import TooltipUI + +extension ChatControllerImpl { + func openMessageContextMenu(message: Message, selectAll: Bool, node: ASDisplayNode, frame: CGRect, anyRecognizer: UIGestureRecognizer?, location: CGPoint?) -> Void { + if self.presentationInterfaceState.interfaceState.selectionState != nil { + return + } + let presentationData = self.presentationData + + self.dismissAllTooltips() + + let recognizer: TapLongTapOrDoubleTapGestureRecognizer? = anyRecognizer as? TapLongTapOrDoubleTapGestureRecognizer + let gesture: ContextGesture? = anyRecognizer as? ContextGesture + if let messages = self.chatDisplayNode.historyNode.messageGroupInCurrentHistoryView(message.id) { + (self.view.window as? WindowHost)?.cancelInteractiveKeyboardGestures() + self.chatDisplayNode.cancelInteractiveKeyboardGestures() + var updatedMessages = messages + for i in 0 ..< updatedMessages.count { + if updatedMessages[i].id == message.id { + let message = updatedMessages.remove(at: i) + updatedMessages.insert(message, at: 0) + break + } + } + + guard let topMessage = messages.first else { + return + } + + let _ = combineLatest(queue: .mainQueue(), + self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)), + contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState: self.presentationInterfaceState, context: self.context, messages: updatedMessages, controllerInteraction: self.controllerInteraction, selectAll: selectAll, interfaceInteraction: self.interfaceInteraction, messageNode: node as? ChatMessageItemView), + peerMessageAllowedReactions(context: self.context, message: topMessage), + peerMessageSelectedReactions(context: self.context, message: topMessage), + topMessageReactions(context: self.context, message: topMessage), + ApplicationSpecificNotice.getChatTextSelectionTips(accountManager: self.context.sharedContext.accountManager) + ).startStandalone(next: { [weak self] peer, actions, allowedReactions, selectedReactions, topReactions, chatTextSelectionTips in + guard let self else { + return + } + + /*var hasPremium = false + if case let .user(user) = peer, user.isPremium { + hasPremium = true + }*/ + + var actions = actions + switch actions.content { + case let .list(itemList): + if itemList.isEmpty { + return + } + case .custom, .twoLists: + break + } + + var tip: ContextController.Tip? + + if tip == nil { + let isAd = message.adAttribute != nil + + var isAction = false + for media in message.media { + if media is TelegramMediaAction { + isAction = true + break + } + } + if self.presentationInterfaceState.copyProtectionEnabled && !isAction && !isAd { + if case .scheduledMessages = self.subject { + } else { + var isChannel = false + if let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, case .broadcast = channel.info { + isChannel = true + } + tip = .messageCopyProtection(isChannel: isChannel) + } + } else { + let numberOfComponents = message.text.components(separatedBy: CharacterSet.whitespacesAndNewlines).count + let displayTextSelectionTip = numberOfComponents >= 3 && !message.text.isEmpty && chatTextSelectionTips < 3 && !isAd + if displayTextSelectionTip { + let _ = ApplicationSpecificNotice.incrementChatTextSelectionTips(accountManager: self.context.sharedContext.accountManager).startStandalone() + tip = .textSelection + } + } + } + + if actions.tip == nil { + actions.tip = tip + } + + actions.context = self.context + actions.animationCache = self.controllerInteraction?.presentationContext.animationCache + + if canAddMessageReactions(message: topMessage), let allowedReactions = allowedReactions, !topReactions.isEmpty { + actions.reactionItems = topReactions.map(ReactionContextItem.reaction) + actions.selectedReactionItems = selectedReactions.reactions + + if let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, case .broadcast = channel.info { + actions.alwaysAllowPremiumReactions = true + } + + if !actions.reactionItems.isEmpty { + let reactionItems: [EmojiComponentReactionItem] = actions.reactionItems.compactMap { item -> EmojiComponentReactionItem? in + switch item { + case let .reaction(reaction): + return EmojiComponentReactionItem(reaction: reaction.reaction.rawValue, file: reaction.stillAnimation) + default: + return nil + } + } + + var allReactionsAreAvailable = false + switch allowedReactions { + case .set: + allReactionsAreAvailable = false + case .all: + allReactionsAreAvailable = true + } + + if let channel = self.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramChannel, case .broadcast = channel.info { + allReactionsAreAvailable = false + } + + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) + if premiumConfiguration.isPremiumDisabled { + allReactionsAreAvailable = false + } + + if allReactionsAreAvailable { + actions.getEmojiContent = { [weak self] animationCache, animationRenderer in + guard let self else { + preconditionFailure() + } + + return EmojiPagerContentComponent.emojiInputData( + context: self.context, + animationCache: animationCache, + animationRenderer: animationRenderer, + isStandalone: false, + subject: .reaction(onlyTop: false), + hasTrending: false, + topReactionItems: reactionItems, + areUnicodeEmojiEnabled: false, + areCustomEmojiEnabled: true, + chatPeerId: self.chatLocation.peerId, + selectedItems: selectedReactions.files + ) + } + } else if reactionItems.count > 16 { + actions.getEmojiContent = { [weak self] animationCache, animationRenderer in + guard let self else { + preconditionFailure() + } + + return EmojiPagerContentComponent.emojiInputData( + context: self.context, + animationCache: animationCache, + animationRenderer: animationRenderer, + isStandalone: false, + subject: .reaction(onlyTop: true), + hasTrending: false, + topReactionItems: reactionItems, + areUnicodeEmojiEnabled: false, + areCustomEmojiEnabled: false, + chatPeerId: self.chatLocation.peerId, + selectedItems: selectedReactions.files + ) + } + } + } + } + + self.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() + + let presentationContext = self.controllerInteraction?.presentationContext + + var disableTransitionAnimations = false + var actionsSignal: Signal = .single(actions) + if let entitiesAttribute = message.textEntitiesAttribute { + var emojiFileIds: [Int64] = [] + for entity in entitiesAttribute.entities { + if case let .CustomEmoji(_, fileId) = entity.type { + emojiFileIds.append(fileId) + } + } + + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) + + if !emojiFileIds.isEmpty && !premiumConfiguration.isPremiumDisabled { + tip = .animatedEmoji(text: nil, arguments: nil, file: nil, action: nil) + actions.tip = tip + disableTransitionAnimations = true + + let context = self.context + actionsSignal = .single(actions) + |> then( + context.engine.stickers.resolveInlineStickers(fileIds: emojiFileIds) + |> mapToSignal { files -> Signal in + var packReferences: [StickerPackReference] = [] + var existingIds = Set() + for (_, file) in files { + loop: for attribute in file.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute, let packReference = packReference { + if case let .id(id, _) = packReference, !existingIds.contains(id) { + packReferences.append(packReference) + existingIds.insert(id) + } + break loop + } + } + } + + let action = { [weak self] in + guard let self else { + return + } + self.presentEmojiList(references: packReferences) + } + + if packReferences.count > 1 { + actions.tip = .animatedEmoji(text: presentationData.strings.ChatContextMenu_EmojiSet(Int32(packReferences.count)), arguments: nil, file: nil, action: action) + return .single(actions) + } else if let reference = packReferences.first { + return context.engine.stickers.loadedStickerPack(reference: reference, forceActualized: false) + |> filter { result in + if case .result = result { + return true + } else { + return false + } + } + |> mapToSignal { result in + if case let .result(info, items, _) = result, let presentationContext = presentationContext { + actions.tip = .animatedEmoji( + text: presentationData.strings.ChatContextMenu_EmojiSetSingle(info.title).string, + arguments: TextNodeWithEntities.Arguments( + context: context, + cache: presentationContext.animationCache, + renderer: presentationContext.animationRenderer, + placeholderColor: .clear, + attemptSynchronous: true + ), + file: items.first?.file, + action: action) + return .single(actions) + } else { + return .complete() + } + } + } else { + actions.tip = nil + return .single(actions) + } + } + ) + } + } + + let source: ContextContentSource + if let location = location { + source = .location(ChatMessageContextLocationContentSource(controller: self, location: node.view.convert(node.bounds, to: nil).origin.offsetBy(dx: location.x, dy: location.y))) + } else { + source = .extracted(ChatMessageContextExtractedContentSource(chatNode: self.chatDisplayNode, engine: self.context.engine, message: message, selectAll: selectAll)) + } + + self.canReadHistory.set(false) + + let controller = ContextController(presentationData: self.presentationData, source: source, items: actionsSignal, recognizer: recognizer, gesture: gesture) + controller.dismissed = { [weak self] in + self?.canReadHistory.set(true) + } + controller.immediateItemsTransitionAnimation = disableTransitionAnimations + controller.getOverlayViews = { [weak self] in + guard let self else { + return [] + } + return [self.chatDisplayNode.navigateButtons.view] + } + self.currentContextController = controller + + controller.premiumReactionsSelected = { [weak self, weak controller] in + guard let self else { + return + } + + controller?.dismissWithoutContent() + + let context = self.context + var replaceImpl: ((ViewController) -> Void)? + let controller = PremiumDemoScreen(context: context, subject: .uniqueReactions, action: { + let controller = PremiumIntroScreen(context: context, source: .reactions) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + self.push(controller) + } + + controller.reactionSelected = { [weak self, weak controller] chosenUpdatedReaction, isLarge in + guard let self else { + return + } + + guard let message = messages.first else { + return + } + + controller?.view.endEditing(true) + + let chosenReaction: MessageReaction.Reaction = chosenUpdatedReaction.reaction + + let currentReactions = mergedMessageReactions(attributes: message.attributes)?.reactions ?? [] + var updatedReactions: [MessageReaction.Reaction] = currentReactions.filter(\.isSelected).map(\.value) + var removedReaction: MessageReaction.Reaction? + var isFirst = false + + if let index = updatedReactions.firstIndex(where: { $0 == chosenReaction }) { + removedReaction = chosenReaction + updatedReactions.remove(at: index) + } else { + updatedReactions.append(chosenReaction) + isFirst = !currentReactions.contains(where: { $0.value == chosenReaction }) + } + + /*guard let allowedReactions = allowedReactions else { + itemNode.openMessageContextMenu() + return + } + + switch allowedReactions { + case let .set(set): + if !messageAlreadyHasThisReaction && updatedReactions.contains(where: { !set.contains($0) }) { + itemNode.openMessageContextMenu() + return + } + case .all: + break + }*/ + + if removedReaction == nil, case .custom = chosenReaction { + if let peer = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, case .broadcast = peer.info { + } else { + if !self.presentationInterfaceState.isPremium { + controller?.premiumReactionsSelected?() + return + } + } + } + + self.chatDisplayNode.historyNode.forEachItemNode { itemNode in + if let itemNode = itemNode as? ChatMessageItemView, let item = itemNode.item { + if item.message.id == message.id { + if removedReaction == nil && !updatedReactions.isEmpty { + itemNode.awaitingAppliedReaction = (chosenReaction, { [weak self, weak itemNode] in + guard let self, let controller = controller else { + return + } + if let itemNode = itemNode, let targetView = itemNode.targetReactionView(value: chosenReaction) { + self.chatDisplayNode.messageTransitionNode.addMessageContextController(messageId: item.message.id, contextController: controller) + + var hideTargetButton: UIView? + if isFirst { + hideTargetButton = targetView.superview + } + + controller.dismissWithReaction(value: chosenReaction, targetView: targetView, hideNode: true, animateTargetContainer: hideTargetButton, addStandaloneReactionAnimation: { [weak self] standaloneReactionAnimation in + guard let self else { + return + } + self.chatDisplayNode.messageTransitionNode.addMessageStandaloneReactionAnimation(messageId: item.message.id, standaloneReactionAnimation: standaloneReactionAnimation) + standaloneReactionAnimation.frame = self.chatDisplayNode.bounds + self.chatDisplayNode.addSubnode(standaloneReactionAnimation) + }, completion: { [weak self, weak itemNode, weak targetView] in + guard let self, let itemNode = itemNode, let targetView = targetView else { + return + } + + let _ = self + let _ = itemNode + let _ = targetView + }) + } else { + controller.dismiss() + } + }) + } else { + itemNode.awaitingAppliedReaction = (nil, { + controller?.dismiss() + }) + } + } + } + } + + let mappedUpdatedReactions = updatedReactions.map { reaction -> UpdateMessageReaction in + switch reaction { + case let .builtin(value): + return .builtin(value) + case let .custom(fileId): + var customFile: TelegramMediaFile? + if case let .custom(customFileId, file) = chosenUpdatedReaction, fileId == customFileId { + customFile = file + } + return .custom(fileId: fileId, file: customFile) + } + } + + let _ = updateMessageReactionsInteractively(account: self.context.account, messageId: message.id, reactions: mappedUpdatedReactions, isLarge: isLarge, storeAsRecentlyUsed: true).startStandalone() + } + + self.forEachController({ controller in + if let controller = controller as? TooltipScreen { + controller.dismiss() + } + return true + }) + self.window?.presentInGlobalOverlay(controller) + }) + } + } +} diff --git a/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift b/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift index 8383797963e..2964864ea04 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift @@ -303,7 +303,7 @@ private func generateChatReplyOptionItems(selfController: ChatControllerImpl, ch var quote: EngineMessageReplyQuote? let trimmedText = trimStringWithEntities(string: textSelection.text, entities: textSelection.entities, maxLength: quoteMaxLength(appConfig: selfController.context.currentAppConfiguration.with({ $0 }))) if !trimmedText.string.isEmpty { - quote = EngineMessageReplyQuote(text: trimmedText.string, entities: trimmedText.entities, media: nil) + quote = EngineMessageReplyQuote(text: trimmedText.string, offset: textSelection.offset, entities: trimmedText.entities, media: nil) } selfController.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject(messageId: replySubject.messageId, quote: quote)).withoutSelectionState() }) }) @@ -368,7 +368,7 @@ private func generateChatReplyOptionItems(selfController: ChatControllerImpl, ch var quote: EngineMessageReplyQuote? let trimmedText = trimStringWithEntities(string: textSelection.text, entities: textSelection.entities, maxLength: quoteMaxLength(appConfig: selfController.context.currentAppConfiguration.with({ $0 }))) if !trimmedText.string.isEmpty { - quote = EngineMessageReplyQuote(text: trimmedText.string, entities: trimmedText.entities, media: nil) + quote = EngineMessageReplyQuote(text: trimmedText.string, offset: textSelection.offset, entities: trimmedText.entities, media: nil) } selfController.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject(messageId: replySubject.messageId, quote: quote)).withoutSelectionState() }) }) @@ -493,7 +493,7 @@ private func chatReplyOptions(selfController: ChatControllerImpl, sourceNode: AS var replyQuote: ChatControllerSubject.MessageOptionsInfo.Quote? if let quote = replySubject.quote { - replyQuote = ChatControllerSubject.MessageOptionsInfo.Quote(messageId: replySubject.messageId, text: quote.text) + replyQuote = ChatControllerSubject.MessageOptionsInfo.Quote(messageId: replySubject.messageId, text: quote.text, offset: quote.offset) } selectionState.set(selfController.context.account.postbox.messagesAtIds([replySubject.messageId]) |> map { messages -> ChatControllerSubject.MessageOptionsInfo.SelectionState in diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 4e57bc9a5ae..f19efbab3ab 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -129,6 +129,8 @@ import ChatMessageAnimatedStickerItemNode import ChatMessageBubbleItemNode import ChatNavigationButton import WebsiteType +import ChatQrCodeScreen +import PeerInfoScreen public enum ChatControllerPeekActions { case standard @@ -495,6 +497,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let selectAddMemberDisposable = MetaDisposable() let addMemberDisposable = MetaDisposable() + let joinChannelDisposable = MetaDisposable() var shouldDisplayDownButton = false @@ -814,22 +817,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return false } switch action.action { - case .pinnedMessageUpdated: + case .pinnedMessageUpdated, .gameScore, .setSameChatWallpaper, .giveawayResults: for attribute in message.attributes { if let attribute = attribute as? ReplyMessageAttribute { - strongSelf.navigateToMessage(from: message.id, to: .id(attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote?.text : nil))) + strongSelf.navigateToMessage(from: message.id, to: .id(attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote.flatMap { quote in NavigateToMessageParams.Quote(string: quote.text, offset: quote.offset) } : nil))) break } } case let .photoUpdated(image): openMessageByAction = image != nil - case .gameScore: - for attribute in message.attributes { - if let attribute = attribute as? ReplyMessageAttribute { - strongSelf.navigateToMessage(from: message.id, to: .id(attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote?.text : nil))) - break - } - } case .groupPhoneCall, .inviteToGroupPhoneCall: if let activeCall = strongSelf.presentationInterfaceState.activeGroupCallInfo?.activeCall { strongSelf.joinGroupCall(peerId: message.id.peerId, invite: nil, activeCall: EngineGroupCallDescription(id: activeCall.id, accessHash: activeCall.accessHash, title: activeCall.title, scheduleTimestamp: activeCall.scheduleTimestamp, subscribedToScheduled: activeCall.subscribedToScheduled, isStream: activeCall.isStream)) @@ -938,7 +934,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case .setChatTheme: strongSelf.presentThemeSelection() return true - case let .setChatWallpaper(wallpaper): + case let .setChatWallpaper(wallpaper, _): guard message.effectivelyIncoming(strongSelf.context.account.peerId), let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else { strongSelf.presentThemeSelection() return true @@ -958,7 +954,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } let wallpaperPreviewController = WallpaperGalleryController(context: strongSelf.context, source: .wallpaper(wallpaper, options, [], intensity, nil, nil), mode: .peer(EnginePeer(peer), true)) - wallpaperPreviewController.apply = { [weak wallpaperPreviewController] entry, options, _, _, brightness in + wallpaperPreviewController.apply = { [weak wallpaperPreviewController] entry, options, _, _, brightness, forBoth in var settings: WallpaperSettings? if case let .wallpaper(wallpaper, _) = entry { let baseSettings = wallpaper.settings @@ -970,7 +966,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } settings = WallpaperSettings(blur: options.contains(.blur), motion: options.contains(.motion), colors: baseSettings?.colors ?? [], intensity: intensity, rotation: baseSettings?.rotation) } - let _ = (strongSelf.context.engine.themes.setExistingChatWallpaper(messageId: message.id, settings: settings) + let _ = (strongSelf.context.engine.themes.setExistingChatWallpaper(messageId: message.id, settings: settings, forBoth: forBoth) |> deliverOnMainQueue).startStandalone() Queue.mainQueue().after(0.1) { wallpaperPreviewController?.dismiss() @@ -978,13 +974,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } strongSelf.push(wallpaperPreviewController) return true - case .setSameChatWallpaper: - for attribute in message.attributes { - if let attribute = attribute as? ReplyMessageAttribute { - strongSelf.controllerInteraction?.navigateToMessage(message.id, attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: nil)) - return true - } - } case let .giftPremium(_, _, duration, _, _): strongSelf.chatDisplayNode.dismissInput() let fromPeerId: PeerId = message.author?.id == strongSelf.context.account.peerId ? strongSelf.context.account.peerId : message.id.peerId @@ -1251,398 +1240,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }, openPeerMention: { [weak self] name, progress in self?.openPeerMention(name, progress: progress) }, openMessageContextMenu: { [weak self] message, selectAll, node, frame, anyRecognizer, location in - guard let strongSelf = self, strongSelf.isNodeLoaded else { + guard let self, self.isNodeLoaded else { return } - if strongSelf.presentationInterfaceState.interfaceState.selectionState != nil { - return - } - let presentationData = strongSelf.presentationData - - strongSelf.dismissAllTooltips() - - let recognizer: TapLongTapOrDoubleTapGestureRecognizer? = anyRecognizer as? TapLongTapOrDoubleTapGestureRecognizer - let gesture: ContextGesture? = anyRecognizer as? ContextGesture - if let messages = strongSelf.chatDisplayNode.historyNode.messageGroupInCurrentHistoryView(message.id) { - (strongSelf.view.window as? WindowHost)?.cancelInteractiveKeyboardGestures() - strongSelf.chatDisplayNode.cancelInteractiveKeyboardGestures() - var updatedMessages = messages - for i in 0 ..< updatedMessages.count { - if updatedMessages[i].id == message.id { - let message = updatedMessages.remove(at: i) - updatedMessages.insert(message, at: 0) - break - } - } - - guard let topMessage = messages.first else { - return - } - - let _ = combineLatest(queue: .mainQueue(), - strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: strongSelf.context.account.peerId)), - contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState: strongSelf.presentationInterfaceState, context: strongSelf.context, messages: updatedMessages, controllerInteraction: strongSelf.controllerInteraction, selectAll: selectAll, interfaceInteraction: strongSelf.interfaceInteraction, messageNode: node as? ChatMessageItemView), - peerMessageAllowedReactions(context: strongSelf.context, message: topMessage), - peerMessageSelectedReactions(context: strongSelf.context, message: topMessage), - topMessageReactions(context: strongSelf.context, message: topMessage), - ApplicationSpecificNotice.getChatTextSelectionTips(accountManager: strongSelf.context.sharedContext.accountManager) - ).startStandalone(next: { peer, actions, allowedReactions, selectedReactions, topReactions, chatTextSelectionTips in - guard let strongSelf = self else { - return - } - - /*var hasPremium = false - if case let .user(user) = peer, user.isPremium { - hasPremium = true - }*/ - - var actions = actions - switch actions.content { - case let .list(itemList): - if itemList.isEmpty { - return - } - case .custom, .twoLists: - break - } - - var tip: ContextController.Tip? - - if tip == nil { - let isAd = message.adAttribute != nil - - var isAction = false - for media in message.media { - if media is TelegramMediaAction { - isAction = true - break - } - } - if strongSelf.presentationInterfaceState.copyProtectionEnabled && !isAction && !isAd { - if case .scheduledMessages = strongSelf.subject { - } else { - var isChannel = false - if let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, case .broadcast = channel.info { - isChannel = true - } - tip = .messageCopyProtection(isChannel: isChannel) - } - } else { - let numberOfComponents = message.text.components(separatedBy: CharacterSet.whitespacesAndNewlines).count - let displayTextSelectionTip = numberOfComponents >= 3 && !message.text.isEmpty && chatTextSelectionTips < 3 && !isAd - if displayTextSelectionTip { - let _ = ApplicationSpecificNotice.incrementChatTextSelectionTips(accountManager: strongSelf.context.sharedContext.accountManager).startStandalone() - tip = .textSelection - } - } - } - - if actions.tip == nil { - actions.tip = tip - } - - actions.context = strongSelf.context - actions.animationCache = strongSelf.controllerInteraction?.presentationContext.animationCache - - if canAddMessageReactions(message: topMessage), let allowedReactions = allowedReactions, !topReactions.isEmpty { - actions.reactionItems = topReactions.map(ReactionContextItem.reaction) - actions.selectedReactionItems = selectedReactions.reactions - - if !actions.reactionItems.isEmpty { - let reactionItems: [EmojiComponentReactionItem] = actions.reactionItems.compactMap { item -> EmojiComponentReactionItem? in - switch item { - case let .reaction(reaction): - return EmojiComponentReactionItem(reaction: reaction.reaction.rawValue, file: reaction.stillAnimation) - default: - return nil - } - } - - var allReactionsAreAvailable = false - switch allowedReactions { - case .set: - allReactionsAreAvailable = false - case .all: - allReactionsAreAvailable = true - } - - if let channel = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramChannel, case .broadcast = channel.info { - allReactionsAreAvailable = false - } - - let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) - if premiumConfiguration.isPremiumDisabled { - allReactionsAreAvailable = false - } - - if allReactionsAreAvailable { - actions.getEmojiContent = { animationCache, animationRenderer in - guard let strongSelf = self else { - preconditionFailure() - } - - return EmojiPagerContentComponent.emojiInputData( - context: strongSelf.context, - animationCache: animationCache, - animationRenderer: animationRenderer, - isStandalone: false, - subject: .reaction, - hasTrending: false, - topReactionItems: reactionItems, - areUnicodeEmojiEnabled: false, - areCustomEmojiEnabled: true, - chatPeerId: strongSelf.chatLocation.peerId, - selectedItems: selectedReactions.files - ) - } - } - } - } - - strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() - - let presentationContext = strongSelf.controllerInteraction?.presentationContext - - var disableTransitionAnimations = false - var actionsSignal: Signal = .single(actions) - if let entitiesAttribute = message.textEntitiesAttribute { - var emojiFileIds: [Int64] = [] - for entity in entitiesAttribute.entities { - if case let .CustomEmoji(_, fileId) = entity.type { - emojiFileIds.append(fileId) - } - } - - let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) - - if !emojiFileIds.isEmpty && !premiumConfiguration.isPremiumDisabled { - tip = .animatedEmoji(text: nil, arguments: nil, file: nil, action: nil) - actions.tip = tip - disableTransitionAnimations = true - - actionsSignal = .single(actions) - |> then( - context.engine.stickers.resolveInlineStickers(fileIds: emojiFileIds) - |> mapToSignal { files -> Signal in - var packReferences: [StickerPackReference] = [] - var existingIds = Set() - for (_, file) in files { - loop: for attribute in file.attributes { - if case let .CustomEmoji(_, _, _, packReference) = attribute, let packReference = packReference { - if case let .id(id, _) = packReference, !existingIds.contains(id) { - packReferences.append(packReference) - existingIds.insert(id) - } - break loop - } - } - } - - let action = { - guard let strongSelf = self else { - return - } - strongSelf.presentEmojiList(references: packReferences) - } - - if packReferences.count > 1 { - actions.tip = .animatedEmoji(text: presentationData.strings.ChatContextMenu_EmojiSet(Int32(packReferences.count)), arguments: nil, file: nil, action: action) - return .single(actions) - } else if let reference = packReferences.first { - return context.engine.stickers.loadedStickerPack(reference: reference, forceActualized: false) - |> filter { result in - if case .result = result { - return true - } else { - return false - } - } - |> mapToSignal { result in - if case let .result(info, items, _) = result, let presentationContext = presentationContext { - actions.tip = .animatedEmoji( - text: presentationData.strings.ChatContextMenu_EmojiSetSingle(info.title).string, - arguments: TextNodeWithEntities.Arguments( - context: context, - cache: presentationContext.animationCache, - renderer: presentationContext.animationRenderer, - placeholderColor: .clear, - attemptSynchronous: true - ), - file: items.first?.file, - action: action) - return .single(actions) - } else { - return .complete() - } - } - } else { - actions.tip = nil - return .single(actions) - } - } - ) - } - } - - let source: ContextContentSource - if let location = location { - source = .location(ChatMessageContextLocationContentSource(controller: strongSelf, location: node.view.convert(node.bounds, to: nil).origin.offsetBy(dx: location.x, dy: location.y))) - } else { - source = .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, engine: strongSelf.context.engine, message: message, selectAll: selectAll)) - } - - strongSelf.canReadHistory.set(false) - - let controller = ContextController(presentationData: strongSelf.presentationData, source: source, items: actionsSignal, recognizer: recognizer, gesture: gesture) - controller.dismissed = { [weak self] in - self?.canReadHistory.set(true) - } - controller.immediateItemsTransitionAnimation = disableTransitionAnimations - controller.getOverlayViews = { [weak self] in - guard let strongSelf = self else { - return [] - } - return [strongSelf.chatDisplayNode.navigateButtons.view] - } - strongSelf.currentContextController = controller - - controller.premiumReactionsSelected = { [weak controller] in - guard let strongSelf = self else { - return - } - - controller?.dismissWithoutContent() - - let context = strongSelf.context - var replaceImpl: ((ViewController) -> Void)? - let controller = PremiumDemoScreen(context: context, subject: .uniqueReactions, action: { - let controller = PremiumIntroScreen(context: context, source: .reactions) - replaceImpl?(controller) - }) - replaceImpl = { [weak controller] c in - controller?.replace(with: c) - } - strongSelf.push(controller) - } - - controller.reactionSelected = { [weak controller] chosenUpdatedReaction, isLarge in - guard let strongSelf = self else { - return - } - - guard let message = messages.first else { - return - } - - controller?.view.endEditing(true) - - let chosenReaction: MessageReaction.Reaction = chosenUpdatedReaction.reaction - - let currentReactions = mergedMessageReactions(attributes: message.attributes)?.reactions ?? [] - var updatedReactions: [MessageReaction.Reaction] = currentReactions.filter(\.isSelected).map(\.value) - var removedReaction: MessageReaction.Reaction? - var isFirst = false - - if let index = updatedReactions.firstIndex(where: { $0 == chosenReaction }) { - removedReaction = chosenReaction - updatedReactions.remove(at: index) - } else { - updatedReactions.append(chosenReaction) - isFirst = !currentReactions.contains(where: { $0.value == chosenReaction }) - } - - /*guard let allowedReactions = allowedReactions else { - itemNode.openMessageContextMenu() - return - } - - switch allowedReactions { - case let .set(set): - if !messageAlreadyHasThisReaction && updatedReactions.contains(where: { !set.contains($0) }) { - itemNode.openMessageContextMenu() - return - } - case .all: - break - }*/ - - if removedReaction == nil, case .custom = chosenReaction { - if !strongSelf.presentationInterfaceState.isPremium { - controller?.premiumReactionsSelected?() - return - } - } - - strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in - if let itemNode = itemNode as? ChatMessageItemView, let item = itemNode.item { - if item.message.id == message.id { - if removedReaction == nil && !updatedReactions.isEmpty { - itemNode.awaitingAppliedReaction = (chosenReaction, { [weak itemNode] in - guard let controller = controller else { - return - } - if let itemNode = itemNode, let targetView = itemNode.targetReactionView(value: chosenReaction) { - strongSelf.chatDisplayNode.messageTransitionNode.addMessageContextController(messageId: item.message.id, contextController: controller) - - var hideTargetButton: UIView? - if isFirst { - hideTargetButton = targetView.superview - } - - controller.dismissWithReaction(value: chosenReaction, targetView: targetView, hideNode: true, animateTargetContainer: hideTargetButton, addStandaloneReactionAnimation: { standaloneReactionAnimation in - guard let strongSelf = self else { - return - } - strongSelf.chatDisplayNode.messageTransitionNode.addMessageStandaloneReactionAnimation(messageId: item.message.id, standaloneReactionAnimation: standaloneReactionAnimation) - standaloneReactionAnimation.frame = strongSelf.chatDisplayNode.bounds - strongSelf.chatDisplayNode.addSubnode(standaloneReactionAnimation) - }, completion: { [weak itemNode, weak targetView] in - guard let strongSelf = self, let itemNode = itemNode, let targetView = targetView else { - return - } - - let _ = strongSelf - let _ = itemNode - let _ = targetView - }) - } else { - controller.dismiss() - } - }) - } else { - itemNode.awaitingAppliedReaction = (nil, { - controller?.dismiss() - }) - } - } - } - } - - let mappedUpdatedReactions = updatedReactions.map { reaction -> UpdateMessageReaction in - switch reaction { - case let .builtin(value): - return .builtin(value) - case let .custom(fileId): - var customFile: TelegramMediaFile? - if case let .custom(customFileId, file) = chosenUpdatedReaction, fileId == customFileId { - customFile = file - } - return .custom(fileId: fileId, file: customFile) - } - } - - let _ = updateMessageReactionsInteractively(account: strongSelf.context.account, messageId: message.id, reactions: mappedUpdatedReactions, isLarge: isLarge, storeAsRecentlyUsed: true).startStandalone() - } - - strongSelf.forEachController({ controller in - if let controller = controller as? TooltipScreen { - controller.dismiss() - } - return true - }) - strongSelf.window?.presentInGlobalOverlay(controller) - }) - } + self.openMessageContextMenu(message: message, selectAll: selectAll, node: node, frame: frame, anyRecognizer: anyRecognizer, location: location) }, openMessageReactionContextMenu: { [weak self] message, sourceView, gesture, value in guard let strongSelf = self else { return @@ -2029,6 +1630,33 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } + if !strongSelf.presentationInterfaceState.isPremium && mappedUpdatedReactions.count > strongSelf.context.userLimits.maxReactionsPerMessage { + let _ = (ApplicationSpecificNotice.incrementMultipleReactionsSuggestion(accountManager: strongSelf.context.sharedContext.accountManager) + |> deliverOnMainQueue).startStandalone(next: { [weak self] count in + guard let self else { + return + } + if count < 1 { + let context = self.context + let controller = UndoOverlayController( + presentationData: self.presentationData, + content: .premiumPaywall(title: nil, text: self.presentationData.strings.Chat_Reactions_MultiplePremiumTooltip, customUndoText: nil, timeout: nil, linkAction: nil), + elevatedLayout: false, + action: { [weak self] action in + if case .info = action { + if let self { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .reactions, forceDark: false, dismissed: nil) + self.push(controller) + } + } + return true + } + ) + self.present(controller, in: .current) + } + }) + } + let _ = updateMessageReactionsInteractively(account: strongSelf.context.account, messageId: message.id, reactions: mappedUpdatedReactions, isLarge: false, storeAsRecentlyUsed: false).startStandalone() } }) @@ -2063,46 +1691,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let self else { return } - - let continueNavigation: () -> Void = { [weak self] in - guard let self else { - return - } - self.navigateToMessage(from: fromId, to: .id(id, params), forceInCurrentChat: fromId.peerId == id.peerId) - } - - let _ = (self.context.engine.data.get( - TelegramEngine.EngineData.Item.Peer.Peer(id: id.peerId) - ) - |> deliverOnMainQueue).startStandalone(next: { [weak self] toPeer in - guard let self else { - return - } - - if params.quote != nil { - if let toPeer { - switch toPeer { - case let .channel(channel): - if channel.username == nil && channel.usernames.isEmpty { - switch channel.participationStatus { - case .kicked, .left: - self.controllerInteraction?.attemptedNavigationToPrivateQuote(toPeer._asPeer()) - return - case .member: - break - } - } - default: - break - } - } else { - self.controllerInteraction?.attemptedNavigationToPrivateQuote(nil) - return - } - } - - continueNavigation() - }) + self.navigateToMessage(fromId: fromId, id: id, params: params) }, navigateToMessageStandalone: { [weak self] id in self?.navigateToMessage(from: nil, to: .id(id, NavigateToMessageParams(timestamp: nil, quote: nil)), forceInCurrentChat: false) }, navigateToThreadMessage: { [weak self] peerId, threadId, messageId in @@ -2509,7 +2098,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) |> deliverOnMainQueue).startStrict(error: { error in let controller = ownershipTransferController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, initialError: error, present: { c, a in - strongSelf.present(c, in: .window(.root), with: a) + strongSelf.present(c, in: .window(.root), with: a) }, commit: { password in return context.engine.messages.requestMessageActionCallback(messageId: messageId, isGame: isGame, password: password, data: data) |> afterDisposed { @@ -2920,6 +2509,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }, presentController: { [weak self] controller, arguments in self?.present(controller, in: .window(.root), with: arguments) }, presentControllerInCurrent: { [weak self] controller, arguments in + if controller is UndoOverlayController { + self?.dismissAllTooltips() + } self?.present(controller, in: .current, with: arguments) }, navigationController: { [weak self] in return self?.navigationController as? NavigationController @@ -3536,6 +3128,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor) }, action: { [weak self] controller, f in if let strongSelf = self { + if strongSelf.context.sharedContext.immediateExperimentalUISettings.dustEffect { + strongSelf.chatDisplayNode.historyNode.setCurrentDeleteAnimationCorrelationIds([id]) + } let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: [id], type: .forLocalPeer).startStandalone() } f(.dismissWithoutContent) @@ -3916,7 +3511,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let trimmedText = trimStringWithEntities(string: quoteText, entities: messageTextEntitiesInRange(entities: message.textEntitiesAttribute?.entities ?? [], range: nsRange, onlyQuoteable: true), maxLength: quoteMaxLength(appConfig: strongSelf.context.currentAppConfiguration.with({ $0 }))) if !trimmedText.string.isEmpty { - quoteData = EngineMessageReplyQuote(text: trimmedText.string, entities: trimmedText.entities, media: nil) + quoteData = EngineMessageReplyQuote(text: trimmedText.string, offset: nsRange.location, entities: trimmedText.entities, media: nil) } let replySubject = ChatInterfaceState.ReplyMessageSubject( @@ -4065,7 +3660,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/User"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in f(.dismissWithoutContent) - self?.openPeer(peer: peer, navigation: .info, fromMessage: nil) + self?.openPeer(peer: peer, navigation: .info(nil), fromMessage: nil) })) ] items.append(.action(ContextMenuActionItem(text: isChannel ? strongSelf.presentationData.strings.Conversation_ContextMenuOpenChannel : strongSelf.presentationData.strings.Conversation_ContextMenuSendMessage, icon: { theme in @@ -4142,21 +3737,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let _ = strongSelf.presentVoiceMessageDiscardAlert(action: { let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: id)) - |> mapToSignal { message -> Signal<(EngineMessage.Id, Int32?)?, NoError> in + |> mapToSignal { message -> Signal in if let message { - return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.StatsDatacenterId(id: message.id.peerId)) - |> map { statsDatacenterId -> (EngineMessage.Id, Int32?)? in - return (message.id, statsDatacenterId) - } + return .single(message.id) } else { return .complete() } } - |> deliverOnMainQueue).startStandalone(next: { [weak self] messageIdAndStatsDatacenterId in - guard let strongSelf = self, let (id, statsDatacenterId) = messageIdAndStatsDatacenterId, let statsDatacenterId = statsDatacenterId else { + |> deliverOnMainQueue).startStandalone(next: { [weak self] messageId in + guard let strongSelf = self, let messageId else { return } - strongSelf.push(messageStatsController(context: context, messageId: id, statsDatacenterId: statsDatacenterId)) + strongSelf.push(messageStatsController(context: context, subject: .message(id: messageId))) }) }, delay: true) }, editMessageMedia: { [weak self] messageId, draw in @@ -4312,14 +3904,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } strongSelf.openResolved(result: .join(joinHash), sourceMessageId: nil) }, openWebView: { [weak self] buttonText, url, simple, source in - guard let strongSelf = self, let peerId = strongSelf.chatLocation.peerId, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramUser else { + guard let strongSelf = self, let peerId = strongSelf.chatLocation.peerId, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else { return } strongSelf.chatDisplayNode.dismissInput() - let botName = EnginePeer(peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder) - let botAddress = peer.addressName ?? "" + let botName: String + let botAddress: String + if case let .inline(bot) = source { + botName = bot.compactDisplayTitle + botAddress = bot.addressName ?? "" + } else { + botName = EnginePeer(peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder) + botAddress = peer.addressName ?? "" + } if source == .generic { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { @@ -4557,6 +4156,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.controllerInteraction?.openJoinLink(joinHash) case let .webPage(_, url): self.controllerInteraction?.openUrl(ChatControllerInteraction.OpenUrl(url: url, concealed: false, external: false)) + case let .botApp(peerId, botApp, startParam): + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) + |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in + if let self, let peer { + self.presentBotApp(botApp: botApp, botPeer: peer, payload: startParam) + } + }) } }, openRequestedPeerSelection: { [weak self] messageId, peerType, buttonId in guard let self else { @@ -4934,6 +4540,67 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.push(controller) }) + }, openRecommendedChannelContextMenu: { [weak self] peer, sourceView, gesture in + guard let self else { + return + } + + let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true)) + chatController.canReadHistory.set(false) + + var items: [ContextMenuItem] = [ + .action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_LinkDialogOpen, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ImageEnlarge"), color: theme.actionSheet.primaryTextColor) }, action: { [weak self] _, f in + f(.dismissWithoutContent) + self?.openPeer(peer: peer, navigation: .chat(textInputState: nil, subject: nil, peekData: nil), fromMessage: nil) + })), + ] + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Chat_SimilarChannels_Join, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.actionSheet.primaryTextColor) }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + guard let self else { + return + } + let presentationData = self.presentationData + self.joinChannelDisposable.set(( + self.context.peerChannelMemberCategoriesContextsManager.join(engine: self.context.engine, peerId: peer.id, hash: nil) + |> deliverOnMainQueue + |> afterCompleted { [weak self] in + Queue.mainQueue().async { + if let self { + self.present(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.Chat_SimilarChannels_JoinedChannel(peer.compactDisplayTitle).string, timeout: nil, customUndoText: nil), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { _ in return false }), in: .current) + } + } + } + ).startStrict(error: { [weak self] error in + guard let self else { + return + } + let text: String + switch error { + case .inviteRequestSent: + self.present(UndoOverlayController(presentationData: presentationData, content: .inviteRequestSent(title: presentationData.strings.Group_RequestToJoinSent, text: presentationData.strings.Group_RequestToJoinSentDescriptionGroup), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root)) + return + case .tooMuchJoined: + self.push(oldChannelsController(context: context, intent: .join)) + return + case .tooMuchUsers: + text = self.presentationData.strings.Conversation_UsersTooMuchError + case .generic: + text = self.presentationData.strings.Channel_ErrorAccessDenied + } + self.present(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + })) + }))) + + self.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() + + self.canReadHistory.set(false) + + let contextController = ContextController(presentationData: self.presentationData, source: .controller(ChatContextControllerContentSourceImpl(controller: chatController, sourceView: sourceView, passthroughTouches: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + contextController.dismissed = { [weak self] in + self?.canReadHistory.set(true) + } + self.presentInGlobalOverlay(contextController) }, requestMessageUpdate: { [weak self] id, scroll in if let self { self.chatDisplayNode.historyNode.requestMessageUpdate(id, andScrollToItem: scroll) @@ -5131,16 +4798,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G galleryController.setHintWillBePresentedInPreviewingContext(true) let items: Signal<[ContextMenuItem], NoError> = context.engine.data.get( - TelegramEngine.EngineData.Item.Peer.CanViewStats(id: peer.id), - TelegramEngine.EngineData.Item.Peer.StatsDatacenterId(id: peer.id) + TelegramEngine.EngineData.Item.Peer.CanViewStats(id: peer.id) ) - |> map { canViewStats, statsDatacenterId -> [ContextMenuItem] in + |> map { canViewStats -> [ContextMenuItem] in var items: [ContextMenuItem] = [ .action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_LinkDialogOpen, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in f(.dismissWithoutContent) - self?.navigationButtonAction(.openChatInfo(expandAvatar: true)) + self?.navigationButtonAction(.openChatInfo(expandAvatar: true, recommendedChannels: false)) })) ] if canViewStats { @@ -5155,9 +4821,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let statsController: ViewController if let channel = peer as? TelegramChannel, case .group = channel.info { - statsController = groupStatsController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peer.id, statsDatacenterId: statsDatacenterId) + statsController = groupStatsController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peer.id) } else { - statsController = channelStatsController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peer.id, statsDatacenterId: statsDatacenterId) + statsController = channelStatsController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peer.id) } strongSelf.push(statsController) }))) @@ -5189,7 +4855,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } chatInfoButtonItem.target = self chatInfoButtonItem.action = #selector(self.rightNavigationButtonAction) - self.chatInfoNavigationButton = ChatNavigationButton(action: .openChatInfo(expandAvatar: true), buttonItem: chatInfoButtonItem) + self.chatInfoNavigationButton = ChatNavigationButton(action: .openChatInfo(expandAvatar: true, recommendedChannels: false), buttonItem: chatInfoButtonItem) self.moreBarButton.setContent(.more(MoreHeaderButton.optionsCircleImage(color: self.presentationData.theme.rootController.navigationBar.buttonColor))) self.moreInfoNavigationButton = ChatNavigationButton(action: .toggleInfoPanel, buttonItem: UIBarButtonItem(customDisplayNode: self.moreBarButton)!) @@ -5206,7 +4872,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.navigationItem.titleView = self.chatTitleView self.chatTitleView?.pressed = { [weak self] in - self?.navigationButtonAction(.openChatInfo(expandAvatar: false)) + self?.navigationButtonAction(.openChatInfo(expandAvatar: false, recommendedChannels: false)) } self.updateChatPresentationInterfaceState(animated: false, interactive: false, { state in @@ -6582,7 +6248,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let peerId = self.chatLocation.peerId { uploadingChatWallpaper = self.context.account.pendingPeerMediaUploadManager.uploadingPeerMedia |> map { uploadingPeerMedia -> TelegramWallpaper? in - if let item = uploadingPeerMedia[peerId], case let .wallpaper(wallpaper) = item.content { + if let item = uploadingPeerMedia[peerId], case let .wallpaper(wallpaper, _) = item.content { return wallpaper } else { return nil @@ -6928,6 +6594,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.peerSuggestionsDismissDisposable.dispose() self.selectAddMemberDisposable.dispose() self.addMemberDisposable.dispose() + self.joinChannelDisposable.dispose() self.nextChannelToReadDisposable?.dispose() self.inviteRequestsDisposable.dispose() self.sendAsPeersDisposable?.dispose() @@ -7712,7 +7379,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let context = self.context let threadData: Signal - let isGeneralThreadClosed: Signal + let forumTopicData: Signal if let threadId = self.chatLocation.threadId { let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: threadId) threadData = context.account.postbox.combinedView(keys: [viewKey]) @@ -7726,10 +7393,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return ChatPresentationInterfaceState.ThreadData(title: data.info.title, icon: data.info.icon, iconColor: data.info.iconColor, isOwnedByMe: data.isOwnedByMe, isClosed: data.isClosed) } |> distinctUntilChanged - isGeneralThreadClosed = .single(nil) + forumTopicData = .single(nil) } else { - isGeneralThreadClosed = isForum - |> mapToSignal { isForum -> Signal in + forumTopicData = isForum + |> mapToSignal { isForum -> Signal in if isForum { let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: 1) return context.account.postbox.combinedView(keys: [viewKey]) @@ -7743,9 +7410,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return ChatPresentationInterfaceState.ThreadData(title: data.info.title, icon: data.info.icon, iconColor: data.info.iconColor, isOwnedByMe: data.isOwnedByMe, isClosed: data.isClosed) } |> distinctUntilChanged - |> map { threadData -> Bool? in - return threadData?.isClosed - } } else { return .single(nil) } @@ -7816,8 +7480,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G customEmojiAvailable, isForum, threadData, - isGeneralThreadClosed - ).startStrict(next: { [weak self] cachedDataAndMessages, hasPendingMessages, isTopReplyThreadMessageShown, topPinnedMessage, customEmojiAvailable, isForum, threadData, isGeneralThreadClosed in + forumTopicData + ).startStrict(next: { [weak self] cachedDataAndMessages, hasPendingMessages, isTopReplyThreadMessageShown, topPinnedMessage, customEmojiAvailable, isForum, threadData, forumTopicData in if let strongSelf = self { let (cachedData, messages) = cachedDataAndMessages @@ -7996,7 +7660,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - if strongSelf.presentationInterfaceState.pinnedMessageId != pinnedMessageId || strongSelf.presentationInterfaceState.pinnedMessage != pinnedMessage || strongSelf.presentationInterfaceState.peerIsBlocked != peerIsBlocked || pinnedMessageUpdated || callsDataUpdated || voiceMessagesAvailableUpdated || strongSelf.presentationInterfaceState.slowmodeState != slowmodeState || strongSelf.presentationInterfaceState.activeGroupCallInfo != activeGroupCallInfo || customEmojiAvailable != strongSelf.presentationInterfaceState.customEmojiAvailable || threadData != strongSelf.presentationInterfaceState.threadData || isGeneralThreadClosed != strongSelf.presentationInterfaceState.isGeneralThreadClosed || premiumGiftOptions != strongSelf.presentationInterfaceState.premiumGiftOptions { + if strongSelf.presentationInterfaceState.pinnedMessageId != pinnedMessageId || strongSelf.presentationInterfaceState.pinnedMessage != pinnedMessage || strongSelf.presentationInterfaceState.peerIsBlocked != peerIsBlocked || pinnedMessageUpdated || callsDataUpdated || voiceMessagesAvailableUpdated || strongSelf.presentationInterfaceState.slowmodeState != slowmodeState || strongSelf.presentationInterfaceState.activeGroupCallInfo != activeGroupCallInfo || customEmojiAvailable != strongSelf.presentationInterfaceState.customEmojiAvailable || threadData != strongSelf.presentationInterfaceState.threadData || forumTopicData != strongSelf.presentationInterfaceState.forumTopicData || premiumGiftOptions != strongSelf.presentationInterfaceState.premiumGiftOptions { strongSelf.updateChatPresentationInterfaceState(animated: strongSelf.willAppear, interactive: strongSelf.willAppear, { state in return state .updatedPinnedMessageId(pinnedMessageId) @@ -8008,7 +7672,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G .updatedVoiceMessagesAvailable(voiceMessagesAvailable) .updatedCustomEmojiAvailable(customEmojiAvailable) .updatedThreadData(threadData) - .updatedIsGeneralThreadClosed(isGeneralThreadClosed) + .updatedForumTopicData(forumTopicData) + .updatedIsGeneralThreadClosed(forumTopicData?.isClosed) .updatedPremiumGiftOptions(premiumGiftOptions) .updatedTitlePanelContext({ context in if pinnedMessageId != nil { @@ -8149,6 +7814,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } strongSelf.shouldDisplayDownButton = !offsetAlpha.isZero + strongSelf.controllerInteraction?.recommendedChannelsOpenUp = !strongSelf.shouldDisplayDownButton strongSelf.updateDownButtonVisibility() strongSelf.chatDisplayNode.updatePlainInputSeparatorAlpha(plainInputSeparatorAlpha, transition: .animated(duration: 0.2, curve: .easeInOut)) } @@ -8160,15 +7826,22 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .info(title: nil, text: strongSelf.presentationData.strings.Conversation_MessageDoesntExist, timeout: nil, customUndoText: nil), elevatedLayout: false, action: { _ in return true }), in: .current) } } else if let controllerInteraction = strongSelf.controllerInteraction { - if let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(index.id) { - let highlightedState = ChatInterfaceHighlightedState(messageStableId: message.stableId, quote: toSubject.quote) + var mappedId = index.id + if index.timestamp == 0 { + if case let .replyThread(message) = strongSelf.chatLocation, let channelMessageId = message.channelMessageId { + mappedId = channelMessageId + } + } + + if let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(mappedId) { + let highlightedState = ChatInterfaceHighlightedState(messageStableId: message.stableId, quote: toSubject.quote.flatMap { quote in ChatInterfaceHighlightedState.Quote(string: quote.string, offset: quote.offset) }) controllerInteraction.highlightedState = highlightedState strongSelf.updateItemNodesHighlightedStates(animated: initial) - strongSelf.scrolledToMessageIdValue = ScrolledToMessageId(id: index.id, allowedReplacementDirection: []) + strongSelf.scrolledToMessageIdValue = ScrolledToMessageId(id: mappedId, allowedReplacementDirection: []) var hasQuote = false if let quote = toSubject.quote { - if message.text.contains(quote) { + if message.text.contains(quote.string) { hasQuote = true } else { strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .info(title: nil, text: strongSelf.presentationData.strings.Chat_ToastQuoteNotFound, timeout: nil, customUndoText: nil), elevatedLayout: false, action: { _ in return true }), in: .current) @@ -8852,7 +8525,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G messageId: message.id, quote: nil )) - }).updatedSearch(nil).updatedShowCommands(false) }, completion: { t in + }).updatedReplyMessage(message).updatedSearch(nil).updatedShowCommands(false) }, completion: { t in completion(t, {}) }) strongSelf.updateItemNodesSearchTextHighlightStates() @@ -9095,9 +8768,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } if isAction && (actions.options == .deleteGlobally || actions.options == .deleteLocally) { + if strongSelf.context.sharedContext.immediateExperimentalUISettings.dustEffect { + strongSelf.chatDisplayNode.historyNode.setCurrentDeleteAnimationCorrelationIds(messageIds) + } let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: actions.options == .deleteLocally ? .forLocalPeer : .forEveryone).startStandalone() completion(.dismissWithoutContent) } else if (messages.first?.flags.isSending ?? false) { + if strongSelf.context.sharedContext.immediateExperimentalUISettings.dustEffect { + strongSelf.chatDisplayNode.historyNode.setCurrentDeleteAnimationCorrelationIds(messageIds) + } let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone, deleteAllInGroup: true).startStandalone() completion(.dismissWithoutContent) } else { @@ -9525,7 +9204,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }) }, openPeerInfo: { [weak self] in - self?.navigationButtonAction(.openChatInfo(expandAvatar: false)) + self?.navigationButtonAction(.openChatInfo(expandAvatar: false, recommendedChannels: false)) }, togglePeerNotifications: { [weak self] in if let strongSelf = self, let peerId = strongSelf.chatLocation.peerId { let _ = strongSelf.context.engine.peers.togglePeerMuted(peerId: peerId, threadId: strongSelf.chatLocation.threadId).startStandalone() @@ -10470,6 +10149,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }, presentController: { [weak self] controller, arguments in self?.present(controller, in: .window(.root), with: arguments) }, presentControllerInCurrent: { [weak self] controller, arguments in + if controller is UndoOverlayController { + self?.dismissAllTooltips() + } self?.present(controller, in: .current, with: arguments) }, getNavigationController: { [weak self] in return self?.navigationController as? NavigationController @@ -12403,7 +12085,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G @objc func rightNavigationButtonAction() { if let button = self.rightNavigationButton { - if case let .peer(peerId) = self.chatLocation, case .openChatInfo(expandAvatar: true) = button.action, let storyStats = self.storyStats, storyStats.unseenCount != 0, let avatarNode = self.avatarNode { + if case let .peer(peerId) = self.chatLocation, case .openChatInfo(expandAvatar: true, _) = button.action, let storyStats = self.storyStats, storyStats.unseenCount != 0, let avatarNode = self.avatarNode { self.openStories(peerId: peerId, avatarHeaderNode: nil, avatarNode: avatarNode.avatarNode) } else { self.navigationButtonAction(button.action) @@ -12416,7 +12098,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.moreBarButton.contextAction?(self.moreBarButton.containerNode, nil) } - func beginClearHistory(type: InteractiveHistoryClearingType) { + public func beginClearHistory(type: InteractiveHistoryClearingType) { guard case let .peer(peerId) = self.chatLocation else { return } @@ -12711,7 +12393,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.present(actionSheet, in: .window(.root)) }) } - case let .openChatInfo(expandAvatar): + case let .openChatInfo(expandAvatar, recommendedChannels): let _ = self.presentVoiceMessageDiscardAlert(action: { switch self.chatLocationInfoData { case let .peer(peerView): @@ -12740,7 +12422,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let validLayout = strongSelf.validLayout, validLayout.deviceMetrics.type == .tablet { expandAvatar = false } - if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: peer, mode: .generic, avatarInitiallyExpanded: expandAvatar, fromChat: true, requestsContext: strongSelf.inviteRequestsContext) { + if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: peer, mode: recommendedChannels ? .recommendedChannels : .generic, avatarInitiallyExpanded: expandAvatar, fromChat: true, requestsContext: strongSelf.inviteRequestsContext) { strongSelf.effectiveNavigationController?.pushViewController(infoController) } } @@ -13270,7 +12952,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G openBotApp(allowWrite, false) }, showMore: { [weak self] in if let self { - self.openResolved(result: .peer(botPeer._asPeer(), .info), sourceMessageId: nil) + self.openResolved(result: .peer(botPeer._asPeer(), .info(nil)), sourceMessageId: nil) } }) self.present(controller, in: .window(.root)) @@ -16576,332 +16258,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.navigateToMessage(from: nil, to: messageLocation, scrollPosition: scrollPosition, rememberInStack: false, forceInCurrentChat: forceInCurrentChat, dropStack: dropStack, animated: animated, completion: completion, customPresentProgress: customPresentProgress) } - func navigateToMessage(from fromId: MessageId?, to messageLocation: NavigateToMessageLocation, scrollPosition: ListViewScrollPosition = .center(.bottom), rememberInStack: Bool = true, forceInCurrentChat: Bool = false, dropStack: Bool = false, animated: Bool = true, completion: (() -> Void)? = nil, customPresentProgress: ((ViewController, Any?) -> Void)? = nil, statusSubject: ChatLoadingMessageSubject = .generic) { - if self.isNodeLoaded { - var fromIndex: MessageIndex? - - if let fromId = fromId, let message = self.chatDisplayNode.historyNode.messageInCurrentHistoryView(fromId) { - fromIndex = message.index - } else { - if let message = self.chatDisplayNode.historyNode.anchorMessageInCurrentHistoryView() { - fromIndex = message.index - } - } - - var isScheduledMessages = false - var isPinnedMessages = false - if case .scheduledMessages = self.presentationInterfaceState.subject { - isScheduledMessages = true - } else if case .pinnedMessages = self.presentationInterfaceState.subject { - isPinnedMessages = true - } - - var forceInCurrentChat = forceInCurrentChat - if case let .peer(peerId) = self.chatLocation, messageLocation.peerId == peerId, !isPinnedMessages, !isScheduledMessages { - forceInCurrentChat = true - } - - if isPinnedMessages, let messageId = messageLocation.messageId { - let _ = (combineLatest( - self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: messageId.peerId)), - self.context.engine.messages.getMessagesLoadIfNecessary([messageId], strategy: .local) - |> mapToSignal { result -> Signal<[Message], NoError> in - guard case let .result(result) = result else { - return .complete() - } - return .single(result) - } - ) - |> deliverOnMainQueue).startStandalone(next: { [weak self] peer, messages in - guard let self, let peer = peer else { - return - } - guard let navigationController = self.effectiveNavigationController else { - return - } - - self.dismiss() - - let navigateToLocation: NavigateToChatControllerParams.Location - if let message = messages.first, let threadId = message.threadId, let channel = message.peers[message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum) { - navigateToLocation = .replyThread(ChatReplyThreadMessage(messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) - } else { - navigateToLocation = .peer(peer) - } - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: navigateToLocation, subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil), keepStack: .always)) - }) - } else if case let .peer(peerId) = self.chatLocation, let messageId = messageLocation.messageId, (messageId.peerId != peerId && !forceInCurrentChat) || (isScheduledMessages && messageId.id != 0 && !Namespaces.Message.allScheduled.contains(messageId.namespace)) { - let _ = (self.context.engine.data.get( - TelegramEngine.EngineData.Item.Peer.Peer(id: messageId.peerId), - TelegramEngine.EngineData.Item.Messages.Message(id: messageId) - ) - |> deliverOnMainQueue).startStandalone(next: { [weak self] peer, message in - guard let self, let peer = peer else { - return - } - if let navigationController = self.effectiveNavigationController { - var chatLocation: NavigateToChatControllerParams.Location = .peer(peer) - if case let .channel(channel) = peer, channel.flags.contains(.isForum), let message = message, let threadId = message.threadId { - chatLocation = .replyThread(ChatReplyThreadMessage(messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) - } - - var quote: String? - if case let .id(_, params) = messageLocation { - quote = params.quote - } - - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: chatLocation, subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: quote), timecode: nil), keepStack: .always)) - } - }) - } else if forceInCurrentChat { - if let _ = fromId, let fromIndex = fromIndex, rememberInStack { - self.historyNavigationStack.add(fromIndex) - } - - let scrollFromIndex: MessageIndex? - if let fromIndex = fromIndex { - scrollFromIndex = fromIndex - } else if let message = self.chatDisplayNode.historyNode.lastVisbleMesssage() { - scrollFromIndex = message.index - } else { - scrollFromIndex = nil - } - - if let scrollFromIndex = scrollFromIndex { - if let messageId = messageLocation.messageId, let message = self.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId) { - self.loadingMessage.set(.single(nil)) - self.messageIndexDisposable.set(nil) - - var delayCompletion = true - if self.chatDisplayNode.historyNode.isMessageVisible(id: messageId) { - delayCompletion = false - } - - var quote: String? - if case let .id(_, params) = messageLocation { - quote = params.quote - } - self.chatDisplayNode.historyNode.scrollToMessage(from: scrollFromIndex, to: message.index, animated: animated, quote: quote, scrollPosition: scrollPosition) - - if delayCompletion { - Queue.mainQueue().after(0.25, { - completion?() - }) - } else { - Queue.mainQueue().justDispatch({ - completion?() - }) - } - - if case let .id(_, params) = messageLocation, let timecode = params.timestamp { - let _ = self.controllerInteraction?.openMessage(message, OpenMessageParams(mode: .timecode(timecode))) - } - } else if case let .index(index) = messageLocation, index.id.id == 0, index.timestamp > 0, case .scheduledMessages = self.presentationInterfaceState.subject { - self.chatDisplayNode.historyNode.scrollToMessage(from: scrollFromIndex, to: index, animated: animated, scrollPosition: scrollPosition) - } else { - if case let .id(messageId, params) = messageLocation, params.timestamp != nil { - self.scheduledScrollToMessageId = (messageId, params) - } - var progress: Promise? - if case let .id(_, params) = messageLocation { - progress = params.progress - } - self.loadingMessage.set(.single(statusSubject) |> delay(0.1, queue: .mainQueue())) - - let searchLocation: ChatHistoryInitialSearchLocation - switch messageLocation { - case let .id(id, _): - searchLocation = .id(id) - case let .index(index): - searchLocation = .index(index) - case .upperBound: - if let peerId = self.chatLocation.peerId { - searchLocation = .index(MessageIndex.upperBound(peerId: peerId)) - } else { - searchLocation = .index(.absoluteUpperBound()) - } - } - var historyView: Signal - historyView = preloadedChatHistoryViewForLocation(ChatHistoryLocationInput(content: .InitialSearch(subject: MessageHistoryInitialSearchSubject(location: searchLocation, quote: nil), count: 50, highlight: true), id: 0), context: self.context, chatLocation: self.chatLocation, subject: self.subject, chatLocationContextHolder: self.chatLocationContextHolder, fixedCombinedReadStates: nil, tagMask: nil, additionalData: []) - - var signal: Signal<(MessageIndex?, Bool), NoError> - signal = historyView - |> mapToSignal { historyView -> Signal<(MessageIndex?, Bool), NoError> in - switch historyView { - case .Loading: - return .single((nil, true)) - case let .HistoryView(view, _, _, _, _, _, _): - for entry in view.entries { - if entry.message.id == messageLocation.messageId { - return .single((entry.message.index, false)) - } - } - if case let .index(index) = searchLocation { - return .single((index, false)) - } - return .single((nil, false)) - } - } - |> take(until: { index in - return SignalTakeAction(passthrough: true, complete: !index.1) - }) - - /*#if DEBUG - signal = .single((nil, true)) |> then(signal |> delay(2.0, queue: .mainQueue())) - #endif*/ - - var cancelImpl: (() -> Void)? - let presentationData = self.presentationData - let displayTime = CACurrentMediaTime() - let progressSignal = Signal { [weak self] subscriber in - if let progress { - progress.set(.single(true)) - return ActionDisposable { - Queue.mainQueue().async() { - progress.set(.single(false)) - } - } - } else if case .generic = statusSubject { - let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { - if CACurrentMediaTime() - displayTime > 1.5 { - cancelImpl?() - } - })) - if let customPresentProgress = customPresentProgress { - customPresentProgress(controller, nil) - } else { - self?.present(controller, in: .window(.root)) - } - return ActionDisposable { [weak controller] in - Queue.mainQueue().async() { - controller?.dismiss() - } - } - } else { - return EmptyDisposable - } - } - |> runOn(Queue.mainQueue()) - |> delay(0.05, queue: Queue.mainQueue()) - let progressDisposable = MetaDisposable() - var progressStarted = false - self.messageIndexDisposable.set((signal - |> afterDisposed { - Queue.mainQueue().async { - progressDisposable.dispose() - } - } - |> deliverOnMainQueue).startStrict(next: { [weak self] index in - if let strongSelf = self, let index = index.0 { - strongSelf.chatDisplayNode.historyNode.scrollToMessage(from: scrollFromIndex, to: index, animated: animated, scrollPosition: scrollPosition) - completion?() - } else if index.1 { - if !progressStarted { - progressStarted = true - progressDisposable.set(progressSignal.start()) - } - } - }, completed: { [weak self] in - if let strongSelf = self { - strongSelf.loadingMessage.set(.single(nil)) - } - })) - cancelImpl = { [weak self] in - if let strongSelf = self { - strongSelf.loadingMessage.set(.single(nil)) - strongSelf.messageIndexDisposable.set(nil) - } - } - } - } else { - completion?() - } - } else { - if let fromIndex = fromIndex { - let searchLocation: ChatHistoryInitialSearchLocation - switch messageLocation { - case let .id(id, _): - searchLocation = .id(id) - case let .index(index): - searchLocation = .index(index) - case .upperBound: - return - } - if let _ = fromId, rememberInStack { - self.historyNavigationStack.add(fromIndex) - } - self.loadingMessage.set(.single(statusSubject) |> delay(0.1, queue: .mainQueue())) - - var quote: String? - if case let .id(_, params) = messageLocation { - quote = params.quote - } - - let historyView = preloadedChatHistoryViewForLocation(ChatHistoryLocationInput(content: .InitialSearch(subject: MessageHistoryInitialSearchSubject(location: searchLocation, quote: quote), count: 50, highlight: true), id: 0), context: self.context, chatLocation: self.chatLocation, subject: self.subject, chatLocationContextHolder: self.chatLocationContextHolder, fixedCombinedReadStates: nil, tagMask: nil, additionalData: []) - var signal: Signal - signal = historyView - |> mapToSignal { historyView -> Signal in - switch historyView { - case .Loading: - return .complete() - case let .HistoryView(view, _, _, _, _, _, _): - for entry in view.entries { - if entry.message.id == messageLocation.messageId { - return .single(entry.message.index) - } - } - return .single(nil) - } - } - |> take(1) - - self.messageIndexDisposable.set((signal |> deliverOnMainQueue).startStrict(next: { [weak self] index in - if let strongSelf = self { - if let index = index { - strongSelf.chatDisplayNode.historyNode.scrollToMessage(from: fromIndex, to: index, animated: animated, scrollPosition: scrollPosition) - completion?() - } else { - let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: messageLocation.peerId)) - |> deliverOnMainQueue).startStandalone(next: { peer in - guard let strongSelf = self, let peer = peer else { - return - } - - if let navigationController = strongSelf.effectiveNavigationController { - var quote: String? - if case let .id(_, params) = messageLocation { - quote = params.quote - } - - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), subject: messageLocation.messageId.flatMap { .message(id: .id($0), highlight: ChatControllerSubject.MessageHighlight(quote: quote), timecode: nil) })) - } - }) - completion?() - } - } - }, completed: { [weak self] in - if let strongSelf = self { - strongSelf.loadingMessage.set(.single(nil)) - } - })) - } else { - let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: messageLocation.peerId)) - |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in - guard let self, let peer = peer else { - return - } - if let navigationController = self.effectiveNavigationController { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), subject: messageLocation.messageId.flatMap { .message(id: .id($0), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil) })) - } - completion?() - }) - } - } - } else { - completion?() - } - } - // MARK: Nicegram (cloud + asCopy) func forwardMessages(messageIds: [MessageId], options: ChatInterfaceForwardOptionsState? = nil, resetCurrent: Bool = false, cloud: Bool = false, asCopy: Bool = false) { let _ = (self.context.engine.data.get(EngineDataMap( @@ -17297,8 +16653,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let _ = self.presentVoiceMessageDiscardAlert(action: { if case let .peer(currentPeerId) = self.chatLocation, peer?.id == currentPeerId { switch navigation { - case .info: - self.navigationButtonAction(.openChatInfo(expandAvatar: expandAvatar)) + case let .info(params): + var recommendedChannels = false + if let params, params.switchToRecommendedChannels { + recommendedChannels = true + } + self.navigationButtonAction(.openChatInfo(expandAvatar: expandAvatar, recommendedChannels: recommendedChannels)) case let .chat(textInputState, _, _): if let textInputState = textInputState { self.updateChatPresentationInterfaceState(animated: true, interactive: true, { @@ -17347,6 +16707,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let fromReactionMessageId = fromReactionMessageId { mode = .reaction(fromReactionMessageId) } + if case let .info(params) = navigation, let params, params.switchToRecommendedChannels { + mode = .recommendedChannels + } var expandAvatar = expandAvatar if peer.smallProfileImage == nil { expandAvatar = false @@ -17924,7 +17287,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }, dismissInput: { [weak self] in self?.chatDisplayNode.dismissInput() - }, contentContext: nil, progress: progress) + }, contentContext: nil, progress: progress, completion: nil) } func openUrl(_ url: String, concealed: Bool, forceExternal: Bool = false, skipUrlAuth: Bool = false, skipConcealedAlert: Bool = false, message: Message? = nil, allowInlineWebpageResolution: Bool = false, progress: Promise? = nil, commit: @escaping () -> Void = {}) { @@ -18039,6 +17402,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let _ = strongSelf.context.engine.messages.deleteAllMessagesWithAuthor(peerId: peerId, authorId: author.id, namespace: Namespaces.Message.Cloud).startStandalone() let _ = strongSelf.context.engine.messages.clearAuthorHistory(peerId: peerId, memberId: author.id).startStandalone() } else if actions.contains(0) { + if strongSelf.context.sharedContext.immediateExperimentalUISettings.dustEffect { + strongSelf.chatDisplayNode.historyNode.setCurrentDeleteAnimationCorrelationIds(messageIds) + } let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).startStandalone() } if actions.contains(1) { @@ -18077,6 +17443,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G actionSheet?.dismissAnimated() if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) + if strongSelf.context.sharedContext.immediateExperimentalUISettings.dustEffect { + strongSelf.chatDisplayNode.historyNode.setCurrentDeleteAnimationCorrelationIds(messageIds) + } let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).startStandalone() } })) @@ -18103,15 +17472,44 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } contextItems.append(.action(ContextMenuActionItem(text: globalTitle, textColor: .destructive, icon: { _ in nil }, action: { [weak self] _, f in if let strongSelf = self { - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) - let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).startStandalone() - f(.dismissWithoutContent) + var giveaway: TelegramMediaGiveaway? + for messageId in messageIds { + if let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId) { + if let media = message.media.first(where: { $0 is TelegramMediaGiveaway }) as? TelegramMediaGiveaway { + giveaway = media + break + } + } + } + let commit = { + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) + if strongSelf.context.sharedContext.immediateExperimentalUISettings.dustEffect { + strongSelf.chatDisplayNode.historyNode.setCurrentDeleteAnimationCorrelationIds(messageIds) + } + let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).startStandalone() + } + if let giveaway { + Queue.mainQueue().after(0.2) { + let dateString = stringForDate(timestamp: giveaway.untilDate, timeZone: .current, strings: strongSelf.presentationData.strings) + strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: strongSelf.presentationData.strings.Chat_Giveaway_DeleteConfirmation_Title, text: strongSelf.presentationData.strings.Chat_Giveaway_DeleteConfirmation_Text(dateString).string, actions: [TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.Common_Delete, action: { + commit() + }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { + })], parseMarkdown: true), in: .window(.root)) + } + f(.default) + } else { + commit() + f(.dismissWithoutContent) + } } }))) items.append(ActionSheetButtonItem(title: globalTitle, color: .destructive, action: { [weak self, weak actionSheet] in actionSheet?.dismissAnimated() if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) + if strongSelf.context.sharedContext.immediateExperimentalUISettings.dustEffect { + strongSelf.chatDisplayNode.historyNode.setCurrentDeleteAnimationCorrelationIds(messageIds) + } let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).startStandalone() } })) @@ -18131,18 +17529,35 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } } - contextItems.append(.action(ContextMenuActionItem(text: localOptionText, textColor: .destructive, icon: { _ in nil }, action: { [weak self] _, f in + contextItems.append(.action(ContextMenuActionItem(text: localOptionText, textColor: .destructive, icon: { _ in nil }, action: { [weak self] c, f in if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) - let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: unsendPersonalMessages ? .forEveryone : .forLocalPeer).startStandalone() - f(.dismissWithoutContent) + + if strongSelf.context.sharedContext.immediateExperimentalUISettings.dustEffect { + c.dismiss(completion: { [weak strongSelf] in + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1, execute: { + guard let strongSelf else { + return + } + strongSelf.chatDisplayNode.historyNode.setCurrentDeleteAnimationCorrelationIds(messageIds) + let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: unsendPersonalMessages ? .forEveryone : .forLocalPeer).startStandalone() + }) + }) + } else { + f(.dismissWithoutContent) + let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: unsendPersonalMessages ? .forEveryone : .forLocalPeer).startStandalone() + } } }))) items.append(ActionSheetButtonItem(title: localOptionText, color: .destructive, action: { [weak self, weak actionSheet] in actionSheet?.dismissAnimated() if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) + if strongSelf.context.sharedContext.immediateExperimentalUISettings.dustEffect { + strongSelf.chatDisplayNode.historyNode.setCurrentDeleteAnimationCorrelationIds(messageIds) + } let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: unsendPersonalMessages ? .forEveryone : .forLocalPeer).startStandalone() + } })) } @@ -19253,9 +18668,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } let controller = WallpaperGalleryController(context: strongSelf.context, source: .asset(asset), mode: .peer(EnginePeer(peer), false)) controller.navigationPresentation = .modal - controller.apply = { [weak self] wallpaper, options, editedImage, cropRect, brightness in + 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, completion: { + 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() }) @@ -19289,14 +18704,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self, let peerId else { return } - let _ = strongSelf.context.engine.themes.setChatWallpaper(peerId: peerId, wallpaper: nil).startStandalone() + let _ = strongSelf.context.engine.themes.setChatWallpaper(peerId: peerId, wallpaper: nil, forBoth: false).startStandalone() }, completion: { [weak self] emoticon in guard let strongSelf = self, let peerId else { return } if canResetWallpaper && emoticon != nil { - let _ = context.engine.themes.setChatWallpaper(peerId: peerId, wallpaper: nil).startStandalone() + let _ = context.engine.themes.setChatWallpaper(peerId: peerId, wallpaper: nil, forBoth: false).startStandalone() } strongSelf.themeEmoticonAndDarkAppearancePreviewPromise.set(.single((emoticon ?? "", nil))) let _ = context.engine.themes.setChatTheme(peerId: peerId, emoticon: emoticon).startStandalone(completed: { [weak self] in @@ -19409,7 +18824,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - func activateSearch(domain: ChatSearchDomain = .everything, query: String = "") { + public func activateSearch(domain: ChatSearchDomain = .everything, query: String = "") { self.focusOnSearchAfterAppearance = (domain, query) self.interfaceInteraction?.beginMessageSearch(domain, query) } @@ -19550,6 +18965,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G final class ChatContextControllerContentSourceImpl: ContextControllerContentSource { let controller: ViewController weak var sourceNode: ASDisplayNode? + weak var sourceView: UIView? let sourceRect: CGRect? let navigationController: NavigationController? = nil @@ -19563,11 +18979,21 @@ final class ChatContextControllerContentSourceImpl: ContextControllerContentSour self.passthroughTouches = passthroughTouches } + init(controller: ViewController, sourceView: UIView?, sourceRect: CGRect? = nil, passthroughTouches: Bool) { + self.controller = controller + self.sourceView = sourceView + self.sourceRect = sourceRect + self.passthroughTouches = passthroughTouches + } + func transitionInfo() -> ContextControllerTakeControllerInfo? { + let sourceView = self.sourceView let sourceNode = self.sourceNode let sourceRect = self.sourceRect return ContextControllerTakeControllerInfo(contentAreaInScreenSpace: CGRect(origin: CGPoint(), size: CGSize(width: 10.0, height: 10.0)), sourceNode: { [weak sourceNode] in - if let sourceNode = sourceNode { + if let sourceView = sourceView { + return (sourceView, sourceRect ?? sourceView.bounds) + } else if let sourceNode = sourceNode { return (sourceNode.view, sourceRect ?? sourceNode.bounds) } else { return nil @@ -19678,38 +19104,3 @@ func peerMessageSelectedReactions(context: AccountContext, message: Message) -> return (reactions, result) } } - -final class ChatControllerNavigationData: CustomViewControllerNavigationData { - let peerId: PeerId - let threadId: Int64? - - init(peerId: PeerId, threadId: Int64?) { - self.peerId = peerId - self.threadId = threadId - } - - func combine(summary: CustomViewControllerNavigationDataSummary?) -> CustomViewControllerNavigationDataSummary? { - if let summary = summary as? ChatControllerNavigationDataSummary { - return summary.adding(peerNavigationItem: ChatNavigationStackItem(peerId: self.peerId, threadId: threadId)) - } else { - return ChatControllerNavigationDataSummary(peerNavigationItems: [ChatNavigationStackItem(peerId: self.peerId, threadId: threadId)]) - } - } -} - -final class ChatControllerNavigationDataSummary: CustomViewControllerNavigationDataSummary { - let peerNavigationItems: [ChatNavigationStackItem] - - init(peerNavigationItems: [ChatNavigationStackItem]) { - self.peerNavigationItems = peerNavigationItems - } - - func adding(peerNavigationItem: ChatNavigationStackItem) -> ChatControllerNavigationDataSummary { - var peerNavigationItems = self.peerNavigationItems - if let index = peerNavigationItems.firstIndex(of: peerNavigationItem) { - peerNavigationItems.removeSubrange(0 ... index) - } - peerNavigationItems.insert(peerNavigationItem, at: 0) - return ChatControllerNavigationDataSummary(peerNavigationItems: peerNavigationItems) - } -} diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 58fdf565bd4..8baa4336a3d 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -49,6 +49,7 @@ import ManagedDiceAnimationNode import ChatMessageTransitionNode import ChatLoadingNode import ChatRecentActionsController +import UIKitRuntimeUtils final class VideoNavigationControllerDropContentItem: NavigationControllerDropContentItem { let itemNode: OverlayMediaItemNode @@ -100,13 +101,10 @@ private struct ChatControllerNodeDerivedLayoutState { } class HistoryNodeContainer: ASDisplayNode { - private(set) var secretContainer: UIView? - public var isSecret: Bool = false { + var isSecret: Bool { didSet { if self.isSecret != oldValue { - if self.isNodeLoaded { - (self.view as? UITextField)?.isSecureTextEntry = self.isSecret - } + setLayerDisableScreenshots(self.layer, self.isSecret) } } } @@ -116,36 +114,10 @@ class HistoryNodeContainer: ASDisplayNode { super.init() - self.setViewBlock { - let captureProtectedView = UITextField(frame: CGRect()) - captureProtectedView.isSecureTextEntry = self.isSecret - self.secretContainer = captureProtectedView.subviews.first - return captureProtectedView - } - - let _ = self.view - } - - override func addSubnode(_ subnode: ASDisplayNode) { - if let secretContainer = self.secretContainer { - secretContainer.addSubnode(subnode) - } else { - super.addSubnode(subnode) - } - } - - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - if let secretContainer = self.secretContainer { - return secretContainer.hitTest(point, with: event) - } else { - return super.hitTest(point, with: event) + if self.isSecret { + setLayerDisableScreenshots(self.layer, self.isSecret) } } - - func updateSize(size: CGSize, transition: ContainedViewLayoutTransition) { - /*if let secretContainer = self.secretContainer { - }*/ - } } class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { @@ -172,7 +144,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { let contentContainerNode: ASDisplayNode let contentDimNode: ASDisplayNode let backgroundNode: WallpaperBackgroundNode - let historyNode: ChatHistoryListNode + let historyNode: ChatHistoryListNodeImpl var blurredHistoryNode: ASImageNode? let historyNodeContainer: ASDisplayNode let loadingNode: ChatLoadingNode @@ -358,7 +330,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { private var isLoadingValue: Bool = false private var isLoadingEarlier: Bool = false private func updateIsLoading(isLoading: Bool, earlier: Bool, animated: Bool) { - let useLoadingPlaceholder = "".isEmpty + let useLoadingPlaceholder = self.chatLocation.peerId?.namespace != Namespaces.Peer.CloudUser let updated = isLoading != self.isLoadingValue || (isLoading && earlier && !self.isLoadingEarlier) @@ -575,7 +547,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { return (messages, Int32(messages.count), false) } - source = .custom(messages: messages, messageId: messageIds.first ?? MessageId(peerId: PeerId(0), namespace: 0, id: 0), quote: reply.quote?.text, 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) }, loadMore: nil) case let .link(link): let messages = link.options |> mapToSignal { options -> Signal<(ChatControllerSubject.LinkOptions, Peer, Message?, [StoryId: CodableEntry]), NoError> in @@ -634,7 +606,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { var mappedQuote: EngineMessageReplyQuote? if let quote = options.replyQuote { - mappedQuote = EngineMessageReplyQuote(text: quote, entities: [], media: nil) + mappedQuote = EngineMessageReplyQuote(text: quote, offset: nil, entities: [], media: nil) } attributes.append(ReplyMessageAttribute(messageId: replyMessage.id, threadMessageId: nil, quote: mappedQuote, isQuote: mappedQuote != nil)) @@ -675,7 +647,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } var getMessageTransitionNode: (() -> ChatMessageTransitionNodeImpl?)? - self.historyNode = ChatHistoryListNode(context: context, updatedPresentationData: controller?.updatedPresentationData ?? (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, tagMask: nil, source: source, subject: subject, controllerInteraction: controllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), messageTransitionNode: { + self.historyNode = ChatHistoryListNodeImpl(context: context, updatedPresentationData: controller?.updatedPresentationData ?? (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, tagMask: nil, source: source, subject: subject, controllerInteraction: controllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), messageTransitionNode: { return getMessageTransitionNode?() }) self.historyNode.rotated = true @@ -683,8 +655,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { //self.historyScrollingArea = SparseDiscreteScrollingArea() //self.historyNode.historyScrollingArea = self.historyScrollingArea - //self.historyNodeContainer = HistoryNodeContainer(isSecret: chatLocation.peerId?.namespace == Namespaces.Peer.SecretChat) - self.historyNodeContainer = ASDisplayNode() + self.historyNodeContainer = HistoryNodeContainer(isSecret: chatLocation.peerId?.namespace == Namespaces.Peer.SecretChat) self.historyNodeContainer.addSubnode(self.historyNode) @@ -1804,10 +1775,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { transition.updateBounds(node: self.historyNodeContainer, bounds: contentBounds) transition.updatePosition(node: self.historyNodeContainer, position: contentBounds.center) - if let historyNodeContainer = self.historyNodeContainer as? HistoryNodeContainer { - historyNodeContainer.updateSize(size: contentBounds.size, transition: transition) - } - transition.updateBounds(node: self.historyNode, bounds: CGRect(origin: CGPoint(), size: contentBounds.size)) transition.updatePosition(node: self.historyNode, position: CGPoint(x: contentBounds.size.width / 2.0, y: contentBounds.size.height / 2.0)) if let blurredHistoryNode = self.blurredHistoryNode { @@ -2665,7 +2632,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { let themeUpdated = presentationReadyUpdated || (self.chatPresentationInterfaceState.theme !== chatPresentationInterfaceState.theme) - self.backgroundNode.update(wallpaper: chatPresentationInterfaceState.chatWallpaper) + self.backgroundNode.update(wallpaper: chatPresentationInterfaceState.chatWallpaper, animated: true) self.historyNode.verticalScrollIndicatorColor = UIColor(white: 0.5, alpha: 0.8) self.loadingPlaceholderNode?.updatePresentationInterfaceState(chatPresentationInterfaceState) @@ -3943,7 +3910,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { final class SnapshotState { let backgroundNode: WallpaperBackgroundNode - fileprivate let historySnapshotState: ChatHistoryListNode.SnapshotState + fileprivate let historySnapshotState: ChatHistoryListNodeImpl.SnapshotState let titleViewSnapshotState: ChatTitleView.SnapshotState? let avatarSnapshotState: ChatAvatarNavigationNode.SnapshotState? let navigationButtonsSnapshotState: ChatHistoryNavigationButtons.SnapshotState @@ -3954,7 +3921,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { fileprivate init( backgroundNode: WallpaperBackgroundNode, - historySnapshotState: ChatHistoryListNode.SnapshotState, + historySnapshotState: ChatHistoryListNodeImpl.SnapshotState, titleViewSnapshotState: ChatTitleView.SnapshotState?, avatarSnapshotState: ChatAvatarNavigationNode.SnapshotState?, navigationButtonsSnapshotState: ChatHistoryNavigationButtons.SnapshotState, diff --git a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift index 2078f00d802..d9eadc9668c 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift @@ -54,32 +54,62 @@ func chatHistoryEntriesForView( } var joinMessage: Message? - if case let .peer(peerId) = location, case let cachedData = cachedData as? CachedChannelData, let invitedOn = cachedData?.invitedOn { - joinMessage = Message( - stableId: UInt32.max - 1000, - stableVersion: 0, - id: MessageId(peerId: peerId, namespace: Namespaces.Message.Local, id: 0), - globallyUniqueId: nil, - groupingKey: nil, - groupInfo: nil, - threadId: nil, - timestamp: invitedOn, - flags: [.Incoming], - tags: [], - globalTags: [], - localTags: [], - forwardInfo: nil, - author: channelPeer, - text: "", - attributes: [], - media: [TelegramMediaAction(action: .joinedByRequest)], - peers: SimpleDictionary(), - associatedMessages: SimpleDictionary(), - associatedMessageIds: [], - associatedMedia: [:], - associatedThreadInfo: nil, - associatedStories: [:] - ) + if (associatedData.subject?.isService ?? false) { + + } else { + if case let .peer(peerId) = location, case let cachedData = cachedData as? CachedChannelData, let invitedOn = cachedData?.invitedOn { + joinMessage = Message( + stableId: UInt32.max - 1000, + stableVersion: 0, + id: MessageId(peerId: peerId, namespace: Namespaces.Message.Local, id: 0), + globallyUniqueId: nil, + groupingKey: nil, + groupInfo: nil, + threadId: nil, + timestamp: invitedOn, + flags: [.Incoming], + tags: [], + globalTags: [], + localTags: [], + forwardInfo: nil, + author: channelPeer, + text: "", + attributes: [], + media: [TelegramMediaAction(action: .joinedByRequest)], + peers: SimpleDictionary(), + associatedMessages: SimpleDictionary(), + associatedMessageIds: [], + associatedMedia: [:], + associatedThreadInfo: nil, + associatedStories: [:] + ) + } else if let peer = channelPeer as? TelegramChannel, case .broadcast = peer.info, case .member = peer.participationStatus, !peer.flags.contains(.isCreator) { + joinMessage = Message( + stableId: UInt32.max - 1000, + stableVersion: 0, + id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Local, id: 0), + globallyUniqueId: nil, + groupingKey: nil, + groupInfo: nil, + threadId: nil, + timestamp: peer.creationDate, + flags: [.Incoming], + tags: [], + globalTags: [], + localTags: [], + forwardInfo: nil, + author: channelPeer, + text: "", + attributes: [], + media: [TelegramMediaAction(action: .joinedChannel)], + peers: SimpleDictionary(), + associatedMessages: SimpleDictionary(), + associatedMessageIds: [], + associatedMedia: [:], + associatedThreadInfo: nil, + associatedStories: [:] + ) + } } var existingGroupStableIds: [UInt32] = [] @@ -101,12 +131,6 @@ func chatHistoryEntriesForView( } } - if let maybeJoinMessage = joinMessage { - if message.timestamp > maybeJoinMessage.timestamp, (!view.holeEarlier || count > 0) { - entries.append(.MessageEntry(maybeJoinMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil))) - joinMessage = nil - } - } count += 1 if let customThreadOutgoingReadState = customThreadOutgoingReadState { @@ -204,7 +228,7 @@ func chatHistoryEntriesForView( 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] }))) } } - + if !groupBucket.isEmpty { assert(groupMessages || reverseGroupedMessages) if reverseGroupedMessages { @@ -223,11 +247,25 @@ func chatHistoryEntriesForView( } } - if let maybeJoinMessage = joinMessage, !view.holeLater { - entries.append(.MessageEntry(maybeJoinMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil))) - joinMessage = nil + if let lowerTimestamp = view.entries.last?.message.timestamp, let upperTimestamp = view.entries.first?.message.timestamp { + if let joinMessage { + var insertAtPosition: Int? + if joinMessage.timestamp >= lowerTimestamp && view.laterId == nil { + insertAtPosition = entries.count + } else if joinMessage.timestamp < lowerTimestamp && joinMessage.timestamp > upperTimestamp { + for i in 0 ..< entries.count { + if let timestamp = entries[i].timestamp, timestamp > joinMessage.timestamp { + insertAtPosition = i + break + } + } + } + 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) + } + } } - + if let maxReadIndex = view.maxReadIndex, includeUnreadEntry { var i = 0 let unreadEntry: ChatHistoryEntry = .UnreadEntry(maxReadIndex, presentationData) @@ -373,7 +411,7 @@ func chatHistoryEntriesForView( if !entries.isEmpty, case let .MessageEntry(lastMessage, _, _, _, _, _) = entries[entries.count - 1], let message = adMessage { var nextAdMessageId: Int32 = 10000 let updatedMessage = Message( - stableId: ChatHistoryListNode.fixedAdMessageStableId, + stableId: ChatHistoryListNodeImpl.fixedAdMessageStableId, stableVersion: message.stableVersion, id: MessageId(peerId: message.id.peerId, namespace: message.id.namespace, id: nextAdMessageId), globallyUniqueId: nil, diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 8d240579a45..72ec6eafa91 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -38,6 +38,7 @@ import ChatMessageItemImpl import ChatMessageItemView import ChatMessageTransitionNode import ChatControllerInteraction +import DustEffect // MARK: Nicegram AiChat private extension ListViewUpdateSizeAndInsets { @@ -56,17 +57,6 @@ struct ChatTopVisibleMessageRange: Equatable { private let historyMessageCount: Int = 44 -public enum ChatHistoryListDisplayHeaders { - case none - case all - case allButLast -} - -public enum ChatHistoryListMode: Equatable { - case bubbles - case list(search: Bool, reversed: Bool, reverseGroups: Bool, displayHeaders: ChatHistoryListDisplayHeaders, hintLinks: Bool, isGlobalSearch: Bool) -} - enum ChatHistoryViewScrollPosition { case unread(index: MessageIndex) case positionRestoration(index: MessageIndex, relativeOffset: CGFloat) @@ -339,7 +329,27 @@ private final class ChatHistoryTransactionOpaqueState { } } -private func extractAssociatedData(chatLocation: ChatLocation, view: MessageHistoryView, automaticDownloadNetworkType: MediaAutoDownloadNetworkType, animatedEmojiStickers: [String: [StickerPackItem]], additionalAnimatedEmojiStickers: [String: [Int: StickerPackItem]], subject: ChatControllerSubject?, currentlyPlayingMessageId: MessageIndex?, isCopyProtectionEnabled: Bool, availableReactions: AvailableReactions?, defaultReaction: MessageReaction.Reaction?, isPremium: Bool, alwaysDisplayTranscribeButton: ChatMessageItemAssociatedData.DisplayTranscribeButton, accountPeer: EnginePeer?, topicAuthorId: EnginePeer.Id?, hasBots: Bool, translateToLanguage: String?, maxReadStoryId: Int32?) -> ChatMessageItemAssociatedData { +private func extractAssociatedData( + chatLocation: ChatLocation, + view: MessageHistoryView, + automaticDownloadNetworkType: MediaAutoDownloadNetworkType, + animatedEmojiStickers: [String: [StickerPackItem]], + additionalAnimatedEmojiStickers: [String: [Int: StickerPackItem]], + subject: ChatControllerSubject?, + currentlyPlayingMessageId: MessageIndex?, + isCopyProtectionEnabled: Bool, + availableReactions: AvailableReactions?, + defaultReaction: MessageReaction.Reaction?, + isPremium: Bool, + alwaysDisplayTranscribeButton: ChatMessageItemAssociatedData.DisplayTranscribeButton, + accountPeer: EnginePeer?, + topicAuthorId: EnginePeer.Id?, + hasBots: Bool, + translateToLanguage: String?, + maxReadStoryId: Int32?, + recommendedChannels: RecommendedChannels?, + audioTranscriptionTrial: AudioTranscription.TrialState +) -> ChatMessageItemAssociatedData { var automaticDownloadPeerId: EnginePeer.Id? var automaticMediaDownloadPeerType: MediaAutoDownloadPeerType = .channel var contactsPeerIds: Set = Set() @@ -393,7 +403,7 @@ private func extractAssociatedData(chatLocation: ChatLocation, view: MessageHist automaticDownloadPeerId = message.messageId.peerId } - return ChatMessageItemAssociatedData(automaticDownloadPeerType: automaticMediaDownloadPeerType, automaticDownloadPeerId: automaticDownloadPeerId, automaticDownloadNetworkType: automaticDownloadNetworkType, isRecentActions: false, subject: subject, contactsPeerIds: contactsPeerIds, channelDiscussionGroup: channelDiscussionGroup, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, currentlyPlayingMessageId: currentlyPlayingMessageId, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, defaultReaction: defaultReaction, isPremium: isPremium, accountPeer: accountPeer, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, topicAuthorId: topicAuthorId, hasBots: hasBots, translateToLanguage: translateToLanguage, maxReadStoryId: maxReadStoryId) + return ChatMessageItemAssociatedData(automaticDownloadPeerType: automaticMediaDownloadPeerType, automaticDownloadPeerId: automaticDownloadPeerId, automaticDownloadNetworkType: automaticDownloadNetworkType, isRecentActions: false, subject: subject, contactsPeerIds: contactsPeerIds, channelDiscussionGroup: channelDiscussionGroup, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, currentlyPlayingMessageId: currentlyPlayingMessageId, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, defaultReaction: defaultReaction, isPremium: isPremium, accountPeer: accountPeer, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, topicAuthorId: topicAuthorId, hasBots: hasBots, translateToLanguage: translateToLanguage, maxReadStoryId: maxReadStoryId, recommendedChannels: recommendedChannels, audioTranscriptionTrial: audioTranscriptionTrial) } private extension ChatHistoryLocationInput { @@ -435,12 +445,7 @@ private struct ChatHistoryAnimatedEmojiConfiguration { private var nextClientId: Int32 = 1 -public enum ChatHistoryListSource { - case `default` - case custom(messages: Signal<([Message], Int32, Bool), NoError>, messageId: MessageId, quote: String?, loadMore: (() -> Void)?) -} - -public final class ChatHistoryListNode: ListView, ChatHistoryNode { +public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHistoryListNode { static let fixedAdMessageStableId: UInt32 = UInt32.max - 5000 // MARK: Nicegram ChatBanner @@ -670,6 +675,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { private var adMessagesDisposable: Disposable? private var preloadAdPeerId: PeerId? private let preloadAdPeerDisposable = MetaDisposable() + private var didSetupRecommendedChannelsPreload = false + private let preloadRecommendedChannelsDisposable = MetaDisposable() private var seenAdIds: [Data] = [] private var pendingDynamicAdMessages: [Message] = [] private var pendingDynamicAdMessageInterval: Int? @@ -695,7 +702,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { private var toLang: String? - public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal), chatLocation: ChatLocation, chatLocationContextHolder: Atomic, tagMask: MessageTags?, source: ChatHistoryListSource = .default, subject: ChatControllerSubject?, controllerInteraction: ChatControllerInteraction, selectedMessages: Signal?, NoError>, mode: ChatHistoryListMode = .bubbles, messageTransitionNode: @escaping () -> ChatMessageTransitionNodeImpl? = { nil }) { + private var dustEffectLayer: DustEffectLayer? + + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal), chatLocation: ChatLocation, chatLocationContextHolder: Atomic, tagMask: MessageTags?, source: ChatHistoryListSource, subject: ChatControllerSubject?, controllerInteraction: ChatControllerInteraction, selectedMessages: Signal?, NoError>, mode: ChatHistoryListMode = .bubbles, messageTransitionNode: @escaping () -> ChatMessageTransitionNodeImpl?) { // MARK: Nicegram self.wantTrButton = usetrButton() // @@ -846,7 +855,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { initialSearchLocation = .index(MessageIndex.absoluteUpperBound()) } } - self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(subject: MessageHistoryInitialSearchSubject(location: initialSearchLocation, quote: highlight?.quote), count: historyMessageCount, highlight: highlight != nil), id: 0) + self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(subject: MessageHistoryInitialSearchSubject(location: initialSearchLocation, quote: (highlight?.quote).flatMap { quote in MessageHistoryInitialSearchSubject.Quote(string: quote.string, offset: quote.offset) }), count: historyMessageCount, highlight: highlight != nil), id: 0) } else if let subject = subject, case let .pinnedMessages(maybeMessageId) = subject, let messageId = maybeMessageId { self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(subject: MessageHistoryInitialSearchSubject(location: .id(messageId), quote: nil), count: historyMessageCount, highlight: true), id: 0) } else { @@ -1003,6 +1012,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { self.canReadHistoryDisposable?.dispose() self.loadedMessagesFromCachedDataDisposable?.dispose() self.preloadAdPeerDisposable.dispose() + self.preloadRecommendedChannelsDisposable.dispose() self.refreshDisplayedItemRangeTimer?.invalidate() self.genericReactionEffectDisposable?.dispose() self.adMessagesDisposable?.dispose() @@ -1126,7 +1136,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { let scrollPosition: ChatHistoryViewScrollPosition? if isFirstTime, let messageIndex = messages.first(where: { $0.id == at })?.index { - scrollPosition = .index(subject: MessageHistoryScrollToSubject(index: .message(messageIndex), quote: quote), position: .center(.bottom), directionHint: .Down, animated: false, highlight: false, displayLink: false) + scrollPosition = .index(subject: MessageHistoryScrollToSubject(index: .message(messageIndex), quote: quote.flatMap { quote in MessageHistoryScrollToSubject.Quote(string: quote.text, offset: quote.offset) }), position: .center(.bottom), directionHint: .Down, animated: false, highlight: false, displayLink: false) isFirstTime = false } else { scrollPosition = nil @@ -1308,7 +1318,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { self.pendingRemovedMessagesPromise.get(), self.currentlyPlayingMessageIdPromise.get(), self.scrollToMessageIdPromise.get(), - self.chatHasBotsPromise.get() + self.chatHasBotsPromise.get(), + self.allAdMessagesPromise.get() ) let maxReadStoryId: Signal @@ -1329,6 +1340,15 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { maxReadStoryId = .single(nil) } + let recommendedChannels: Signal + if let peerId = self.chatLocation.peerId, peerId.namespace == Namespaces.Peer.CloudChannel { + recommendedChannels = self.context.engine.peers.recommendedChannels(peerId: peerId) + } else { + recommendedChannels = .single(nil) + } + + let audioTranscriptionTrial = self.context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.AudioTranscriptionTrial()) + let messageViewQueue = Queue.mainQueue() let historyViewTransitionDisposable = combineLatest(queue: messageViewQueue, historyViewUpdate, @@ -1346,11 +1366,12 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { audioTranscriptionSuggestion, promises, topicAuthorId, - self.allAdMessagesPromise.get(), translationState, - maxReadStoryId - ).startStrict(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, animatedEmojiStickers, additionalAnimatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState, availableReactions, defaultReaction, accountPeer, suggestAudioTranscription, promises, topicAuthorId, allAdMessages, translationState, maxReadStoryId in - let (historyAppearsCleared, pendingUnpinnedAllMessages, pendingRemovedMessages, currentlyPlayingMessageIdAndType, scrollToMessageId, chatHasBots) = promises + maxReadStoryId, + recommendedChannels, + audioTranscriptionTrial + ).startStrict(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, animatedEmojiStickers, additionalAnimatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState, availableReactions, defaultReaction, accountPeer, suggestAudioTranscription, promises, topicAuthorId, translationState, maxReadStoryId, recommendedChannels, audioTranscriptionTrial in + let (historyAppearsCleared, pendingUnpinnedAllMessages, pendingRemovedMessages, currentlyPlayingMessageIdAndType, scrollToMessageId, chatHasBots, allAdMessages) = promises func applyHole() { Queue.mainQueue().async { @@ -1404,7 +1425,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { initialSearchLocation = .index(.absoluteUpperBound()) } } - strongSelf.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(subject: MessageHistoryInitialSearchSubject(location: initialSearchLocation, quote: highlight?.quote), count: historyMessageCount, highlight: highlight != nil), id: (strongSelf.chatHistoryLocationValue?.id).flatMap({ $0 + 1 }) ?? 0) + strongSelf.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(subject: MessageHistoryInitialSearchSubject(location: initialSearchLocation, quote: (highlight?.quote).flatMap { quote in MessageHistoryInitialSearchSubject.Quote(string: quote.string, offset: quote.offset) }), count: historyMessageCount, highlight: highlight != nil), id: (strongSelf.chatHistoryLocationValue?.id).flatMap({ $0 + 1 }) ?? 0) } else if let subject = subject, case let .pinnedMessages(maybeMessageId) = subject, let messageId = maybeMessageId { strongSelf.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(subject: MessageHistoryInitialSearchSubject(location: .id(messageId), quote: nil), count: historyMessageCount, highlight: true), id: (strongSelf.chatHistoryLocationValue?.id).flatMap({ $0 + 1 }) ?? 0) } else if var chatHistoryLocation = strongSelf.chatHistoryLocationValue { @@ -1473,7 +1494,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { reverseGroups = reverseGroupsValue } - var isCopyProtectionEnabled: Bool = data.initialData?.peer?.isCopyProtectionEnabled ?? false + var isCopyProtectionEnabled: Bool = data.initialData?.peer?.isCopyProtectionEnabled ?? false for entry in view.additionalData { if case let .peer(_, maybePeer) = entry, let peer = maybePeer { isCopyProtectionEnabled = peer.isCopyProtectionEnabled @@ -1505,7 +1526,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { translateToLanguage = languageCode } - let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageIdAndType?.0, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, defaultReaction: defaultReaction, isPremium: isPremium, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, accountPeer: accountPeer, topicAuthorId: topicAuthorId, hasBots: chatHasBots, translateToLanguage: translateToLanguage, maxReadStoryId: maxReadStoryId) + let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageIdAndType?.0, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, defaultReaction: defaultReaction, isPremium: isPremium, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, accountPeer: accountPeer, topicAuthorId: topicAuthorId, hasBots: chatHasBots, translateToLanguage: translateToLanguage, maxReadStoryId: maxReadStoryId, recommendedChannels: recommendedChannels, audioTranscriptionTrial: audioTranscriptionTrial) let filteredEntries = chatHistoryEntriesForView( location: chatLocation, @@ -1631,6 +1652,24 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } isFirstTime = false } + + if let strongSelf = self { + if let recommendedChannels, !recommendedChannels.channels.isEmpty && !recommendedChannels.isHidden { + if !strongSelf.didSetupRecommendedChannelsPreload { + strongSelf.didSetupRecommendedChannelsPreload = true + let preloadDisposable = DisposableSet() + for channel in recommendedChannels.channels.prefix(5) { + preloadDisposable.add(strongSelf.context.account.viewTracker.polledChannel(peerId: channel.peer.id).startStrict()) + preloadDisposable.add(strongSelf.context.account.addAdditionalPreloadHistoryPeerId(peerId: channel.peer.id)) + } + strongSelf.preloadRecommendedChannelsDisposable.set(preloadDisposable) + } + } else { + strongSelf.didSetupRecommendedChannelsPreload = false + strongSelf.preloadRecommendedChannelsDisposable.set(nil) + } + } + if let strongSelf = self, updatedScrollPosition == nil, case .InteractiveChanges = reason, case let .known(offset) = strongSelf.visibleContentOffset(), abs(offset) <= 0.9, let previous = previous { var fillsScreen = true @@ -1672,6 +1711,34 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } } + if let strongSelf = self, updatedScrollPosition == nil, case .InteractiveChanges = reason, let previous = previous, case let .known(offset) = strongSelf.visibleContentOffset(), abs(offset) <= 320.0 { + var hadJoin = false + var hadAd = false + for entry in previous.filteredEntries.reversed() { + if case let .MessageEntry(message, _, _, _, _, _) = entry { + if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case .joinedChannel = action.action { + hadJoin = true + break + } else if message.adAttribute != nil { + hadAd = true + } + } + } + + if !hadJoin && hadAd { + for entry in processedView.filteredEntries.reversed() { + if case let .MessageEntry(message, _, _, _, _, _) = entry { + if message.adAttribute == nil { + if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case .joinedChannel = action.action { + updatedScrollPosition = .index(subject: MessageHistoryScrollToSubject(index: .message(message.index), quote: nil), position: .top(0.0), directionHint: .Up, animated: true, highlight: false, displayLink: false) + } + break + } + } + } + } + } + var forceUpdateAll = false if let previous = previous, previous.associatedData.isPremium != processedView.associatedData.isPremium { forceUpdateAll = true @@ -2172,7 +2239,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } else if let attribute = attribute as? ReactionsMessageAttribute, attribute.hasUnseen { hasUnseenReactions = true } else if let attribute = attribute as? AdMessageAttribute { - if message.stableId != ChatHistoryListNode.fixedAdMessageStableId { + if message.stableId != ChatHistoryListNodeImpl.fixedAdMessageStableId { visibleAdOpaqueIds.append(attribute.opaqueId) } } else if let _ = attribute as? ReplyStoryAttribute { @@ -2660,8 +2727,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } } - public func scrollToMessage(from fromIndex: MessageIndex, to toIndex: MessageIndex, animated: Bool, highlight: Bool = true, quote: String? = nil, scrollPosition: ListViewScrollPosition = .center(.bottom)) { - self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Scroll(subject: MessageHistoryScrollToSubject(index: .message(toIndex), quote: quote), anchorIndex: .message(toIndex), sourceIndex: .message(fromIndex), scrollPosition: scrollPosition, animated: animated, highlight: highlight), id: self.takeNextHistoryLocationId()) + public func scrollToMessage(from fromIndex: MessageIndex, to toIndex: MessageIndex, animated: Bool, highlight: Bool = true, quote: (string: String, offset: Int?)? = nil, scrollPosition: ListViewScrollPosition = .center(.bottom)) { + self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Scroll(subject: MessageHistoryScrollToSubject(index: .message(toIndex), quote: quote.flatMap { quote in MessageHistoryScrollToSubject.Quote(string: quote.string, offset: quote.offset) }), anchorIndex: .message(toIndex), sourceIndex: .message(fromIndex), scrollPosition: scrollPosition, animated: animated, highlight: highlight), id: self.takeNextHistoryLocationId()) } public func anchorMessageInCurrentHistoryView() -> Message? { @@ -2875,11 +2942,115 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { self.hasActiveTransition = true let transition = self.enqueuedHistoryViewTransitions.removeFirst() + var expiredMessageIds = Set() + if let previousHistoryView = self.historyView, transition.options.contains(.AnimateInsertion) { + let demoDustEffect = self.context.sharedContext.immediateExperimentalUISettings.dustEffect + + var existingIds = Set() + for entry in transition.historyView.filteredEntries { + switch entry { + case let .MessageEntry(message, _, _, _, _, _): + if message.autoremoveAttribute != nil || demoDustEffect { + existingIds.insert(message.id) + } + case let .MessageGroupEntry(_, messages, _): + for message in messages { + if message.0.autoremoveAttribute != nil || demoDustEffect { + existingIds.insert(message.0.id) + } + } + default: + break + } + } + let currentTimestamp = Int32(CFAbsoluteTimeGetCurrent()) + for entry in previousHistoryView.filteredEntries { + switch entry { + case let .MessageEntry(message, _, _, _, _, _): + if !existingIds.contains(message.id) { + if let autoremoveAttribute = message.autoremoveAttribute, let countdownBeginTime = autoremoveAttribute.countdownBeginTime { + let exipiresAt = countdownBeginTime + autoremoveAttribute.timeout + if exipiresAt >= currentTimestamp - 1 { + expiredMessageIds.insert(message.id) + } + } else if demoDustEffect { + expiredMessageIds.insert(message.id) + } + } + case let .MessageGroupEntry(_, messages, _): + for message in messages { + if !existingIds.contains(message.0.id) { + if let autoremoveAttribute = message.0.autoremoveAttribute, let countdownBeginTime = autoremoveAttribute.countdownBeginTime { + let exipiresAt = countdownBeginTime + autoremoveAttribute.timeout + if exipiresAt >= currentTimestamp - 1 { + expiredMessageIds.insert(message.0.id) + } else if demoDustEffect { + expiredMessageIds.insert(message.0.id) + } + } + } + } + default: + break + } + } + } + self.currentDeleteAnimationCorrelationIds.formUnion(expiredMessageIds) + + var appliedDeleteAnimationCorrelationIds = Set() + if !self.currentDeleteAnimationCorrelationIds.isEmpty { + var foundItemNodes: [ChatMessageItemView] = [] + self.forEachItemNode { itemNode in + if let itemNode = itemNode as? ChatMessageItemView, let item = itemNode.item { + for (message, _) in item.content { + if self.currentDeleteAnimationCorrelationIds.contains(message.id) { + appliedDeleteAnimationCorrelationIds.insert(message.id) + self.currentDeleteAnimationCorrelationIds.remove(message.id) + foundItemNodes.append(itemNode) + } + } + } + } + if !foundItemNodes.isEmpty { + if self.dustEffectLayer == nil { + let dustEffectLayer = DustEffectLayer() + dustEffectLayer.position = self.bounds.center + dustEffectLayer.bounds = CGRect(origin: CGPoint(), size: self.bounds.size) + self.dustEffectLayer = dustEffectLayer + dustEffectLayer.zPosition = 10.0 + dustEffectLayer.transform = CATransform3DMakeRotation(CGFloat(Double.pi), 0.0, 0.0, 1.0) + self.layer.addSublayer(dustEffectLayer) + dustEffectLayer.becameEmpty = { [weak self] in + guard let self else { + return + } + self.dustEffectLayer?.removeFromSuperlayer() + self.dustEffectLayer = nil + } + } + if let dustEffectLayer = self.dustEffectLayer { + for itemNode in foundItemNodes { + guard let (image, subFrame) = itemNode.makeContentSnapshot() else { + continue + } + let itemFrame = itemNode.layer.convert(subFrame, to: dustEffectLayer) + dustEffectLayer.addItem(frame: itemFrame, image: image) + itemNode.isHidden = true + } + } + } + } + + self.currentAppliedDeleteAnimationCorrelationIds = appliedDeleteAnimationCorrelationIds + let animated = transition.options.contains(.AnimateInsertion) let completion: (Bool, ListViewDisplayedItemRange) -> Void = { [weak self] wasTransformed, visibleRange in if let strongSelf = self { + strongSelf.currentAppliedDeleteAnimationCorrelationIds.removeAll() + var newIncomingReactions: [MessageId: (value: MessageReaction.Reaction, isLarge: Bool)] = [:] + if case .peer = strongSelf.chatLocation, let previousHistoryView = strongSelf.historyView { var updatedIncomingReactions: [MessageId: (value: MessageReaction.Reaction, isLarge: Bool)] = [:] for entry in transition.historyView.filteredEntries { @@ -3878,6 +4049,12 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { func setCurrentSendAnimationCorrelationIds(_ value: Set?) { self.currentSendAnimationCorrelationIds = value } + + private var currentDeleteAnimationCorrelationIds = Set() + func setCurrentDeleteAnimationCorrelationIds(_ value: Set) { + self.currentDeleteAnimationCorrelationIds = value + } + private var currentAppliedDeleteAnimationCorrelationIds = Set() var animationCorrelationMessagesFound: (([Int64: ChatMessageItemView]) -> Void)? @@ -3972,4 +4149,17 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { self.view.layer.animatePosition(from: CGPoint(x: 0.0, y: self.view.bounds.height + snapshotTopInset), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: true, additive: true) } + + override public func customItemDeleteAnimationDuration(itemNode: ListViewItemNode) -> Double? { + if !self.currentAppliedDeleteAnimationCorrelationIds.isEmpty { + if let itemNode = itemNode as? ChatMessageItemView, let item = itemNode.item { + for (message, _) in item.content { + if self.currentAppliedDeleteAnimationCorrelationIds.contains(message.id) { + return 1.5 + } + } + } + } + return nil + } } diff --git a/submodules/TelegramUI/Sources/ChatHistoryNode.swift b/submodules/TelegramUI/Sources/ChatHistoryNode.swift index d6029e2296c..6d8721b3b85 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryNode.swift @@ -5,6 +5,7 @@ import Postbox import SwiftSignalKit import Display import ChatPresentationInterfaceState +import AccountContext public enum ChatHistoryNodeLoadState: Equatable { public enum EmptyType: Equatable { diff --git a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift index 8d3637a45d9..339332657f5 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift @@ -227,7 +227,7 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, ignoreMess preloaded = true - return .HistoryView(view: view, type: reportUpdateType, scrollPosition: .index(subject: MessageHistoryScrollToSubject(index: anchorIndex, quote: searchLocationSubject.quote), position: .center(.bottom), directionHint: .Down, animated: false, highlight: highlight, displayLink: false), flashIndicators: false, originalScrollPosition: nil, initialData: ChatHistoryCombinedInitialData(initialData: initialData, buttonKeyboardMessage: view.topTaggedMessages.first, cachedData: cachedData, cachedDataMessages: cachedDataMessages, readStateData: readStateData), id: location.id) + return .HistoryView(view: view, type: reportUpdateType, scrollPosition: .index(subject: MessageHistoryScrollToSubject(index: anchorIndex, quote: searchLocationSubject.quote.flatMap { quote in MessageHistoryScrollToSubject.Quote(string: quote.string, offset: quote.offset) }), position: .center(.bottom), directionHint: .Down, animated: false, highlight: highlight, displayLink: false), flashIndicators: false, originalScrollPosition: nil, initialData: ChatHistoryCombinedInitialData(initialData: initialData, buttonKeyboardMessage: view.topTaggedMessages.first, cachedData: cachedData, cachedDataMessages: cachedDataMessages, readStateData: readStateData), id: location.id) } } case let .Navigation(index, anchorIndex, count, _): diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 6f249023d48..7ffad99761a 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -586,7 +586,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState var loadStickerSaveStatus: MediaId? var loadCopyMediaResource: MediaResource? var isAction = false - var isGiveawayLaunch = false + var isGiveawayServiceMessage = false var diceEmoji: String? if messages.count == 1 { for media in messages[0].media { @@ -601,8 +601,13 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState } } else if media is TelegramMediaAction || media is TelegramMediaExpiredContent { isAction = true - if let action = media as? TelegramMediaAction, case .giveawayLaunched = action.action { - isGiveawayLaunch = true + if let action = media as? TelegramMediaAction { + switch action.action { + case .giveawayLaunched, .giveawayResults: + isGiveawayServiceMessage = true + default: + break + } } } else if let image = media as? TelegramMediaImage { if !messages[0].containsSecretMedia { @@ -664,7 +669,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState canPin = false } - if isGiveawayLaunch { + if isGiveawayServiceMessage { canReply = false } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift index c1aee0ddbe2..f79b9047726 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift @@ -9,6 +9,7 @@ import ChatPresentationInterfaceState import ChatInputPanelNode import ChatBotStartInputPanelNode import ChatChannelSubscriberInputPanelNode +import ChatMessageSelectionInputPanelNode func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: ChatInputPanelNode?, currentSecondaryPanel: ChatInputPanelNode?, textInputPanelNode: ChatTextInputPanelNode?, interfaceInteraction: ChatPanelInterfaceInteraction?) -> (primary: ChatInputPanelNode?, secondary: ChatInputPanelNode?) { if let renderedPeer = chatPresentationInterfaceState.renderedPeer, renderedPeer.peer?.restrictionText(platform: "ios", contentSettings: context.currentContentSettings.with { $0 }) != nil { @@ -205,6 +206,15 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState return (panel, nil) } } + } else if let replyMessage = chatPresentationInterfaceState.replyMessage, let threadInfo = replyMessage.associatedThreadInfo, threadInfo.isClosed { + if let currentPanel = (currentPanel as? ChatRestrictedInputPanelNode) ?? (currentSecondaryPanel as? ChatRestrictedInputPanelNode) { + return (currentPanel, nil) + } else { + let panel = ChatRestrictedInputPanelNode() + panel.context = context + panel.interfaceInteraction = interfaceInteraction + return (panel, nil) + } } } diff --git a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift index d2ebffd67e2..267bfdc3e2a 100644 --- a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift @@ -889,7 +889,7 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran } } - private let listNode: ChatHistoryListNode + private let listNode: ChatHistoryListNodeImpl private let getContentAreaInScreenSpace: () -> CGRect private let onTransitionEvent: (ContainedViewLayoutTransition) -> Void @@ -907,7 +907,7 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran return !self.animatingItemNodes.isEmpty } - init(listNode: ChatHistoryListNode, getContentAreaInScreenSpace: @escaping () -> CGRect, onTransitionEvent: @escaping (ContainedViewLayoutTransition) -> Void) { + init(listNode: ChatHistoryListNodeImpl, getContentAreaInScreenSpace: @escaping () -> CGRect, onTransitionEvent: @escaping (ContainedViewLayoutTransition) -> Void) { self.listNode = listNode self.getContentAreaInScreenSpace = getContentAreaInScreenSpace self.onTransitionEvent = onTransitionEvent diff --git a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift index 320fa27d16a..9844a5678f8 100644 --- a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift @@ -920,7 +920,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).startStandalone(next: { peer in if let peer = peer { - controllerInteraction.openPeer(peer, .info, nil, .default) + controllerInteraction.openPeer(peer, .info(nil), nil, .default) } }) case let .openWebView(url, simple): diff --git a/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift index 896d08eb1e2..3bc3af51e12 100644 --- a/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift @@ -50,7 +50,11 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode { iconImage = PresentationResourcesChat.chatPanelLockIcon(interfaceState.theme) self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_PanelTopicClosedText, font: Font.regular(15.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) } else if let channel = interfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum), case .peer = interfaceState.chatLocation { - self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_PanelForumModeReplyText, font: Font.regular(15.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) + if let replyMessage = interfaceState.replyMessage, let threadInfo = replyMessage.associatedThreadInfo { + self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_TopicIsClosedLabel(threadInfo.title).string, font: Font.regular(15.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) + } else { + self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_PanelForumModeReplyText, font: Font.regular(15.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) + } } else if let (untilDate, personal) = bannedPermission { if personal && untilDate != 0 && untilDate != Int32.max { self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Conversation_RestrictedTextTimed(stringForFullDate(timestamp: untilDate, strings: interfaceState.strings, dateTimeFormat: interfaceState.dateTimeFormat)).string, font: Font.regular(13.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 6abb714ff05..85c5680d23e 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -815,6 +815,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch self.textInputBackgroundNode.displaysAsynchronously = false self.textInputBackgroundNode.displayWithoutProcessing = true self.textPlaceholderNode = ImmediateTextNode() + self.textPlaceholderNode.contentMode = .topLeft + self.textPlaceholderNode.contentsScale = UIScreenScale self.textPlaceholderNode.maximumNumberOfLines = 1 self.textPlaceholderNode.isUserInteractionEnabled = false @@ -1630,6 +1632,10 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch } } + var updatedPlaceholder: String? + + let themeUpdated = self.presentationInterfaceState?.theme !== interfaceState.theme + var buttonTitleUpdated = false var menuTextSize = self.menuButtonTextNode.frame.size if self.presentationInterfaceState != interfaceState { @@ -1641,7 +1647,6 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch } else if case .commands = interfaceState.botMenuButton, self.menuButtonIconNode.iconState == .app { self.menuButtonIconNode.enqueueState(.menu, animated: false) } - let themeUpdated = previousState?.theme !== interfaceState.theme if themeUpdated { self.menuButtonIconNode.customColor = interfaceState.theme.chat.inputPanel.actionControlForegroundColor self.startButton.updateTheme(SolidRoundedButtonTheme(theme: interfaceState.theme)) @@ -1787,7 +1792,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch let dismissedButtonMessageUpdated = interfaceState.interfaceState.messageActionsState.dismissedButtonKeyboardMessageId != previousState?.interfaceState.messageActionsState.dismissedButtonKeyboardMessageId let replyMessageUpdated = interfaceState.interfaceState.replyMessageSubject != previousState?.interfaceState.replyMessageSubject - if let peer = interfaceState.renderedPeer?.peer, previousState?.renderedPeer?.peer == nil || !peer.isEqual(previousState!.renderedPeer!.peer!) || previousState?.interfaceState.silentPosting != interfaceState.interfaceState.silentPosting || themeUpdated || !self.initializedPlaceholder || previousState?.keyboardButtonsMessage?.id != interfaceState.keyboardButtonsMessage?.id || previousState?.keyboardButtonsMessage?.visibleReplyMarkupPlaceholder != interfaceState.keyboardButtonsMessage?.visibleReplyMarkupPlaceholder || dismissedButtonMessageUpdated || replyMessageUpdated || (previousState?.interfaceState.editMessage == nil) != (interfaceState.interfaceState.editMessage == nil) { + if let peer = interfaceState.renderedPeer?.peer, previousState?.renderedPeer?.peer == nil || !peer.isEqual(previousState!.renderedPeer!.peer!) || previousState?.interfaceState.silentPosting != interfaceState.interfaceState.silentPosting || themeUpdated || !self.initializedPlaceholder || previousState?.keyboardButtonsMessage?.id != interfaceState.keyboardButtonsMessage?.id || previousState?.keyboardButtonsMessage?.visibleReplyMarkupPlaceholder != interfaceState.keyboardButtonsMessage?.visibleReplyMarkupPlaceholder || dismissedButtonMessageUpdated || replyMessageUpdated || (previousState?.interfaceState.editMessage == nil) != (interfaceState.interfaceState.editMessage == nil) || previousState?.forumTopicData != interfaceState.forumTopicData || previousState?.replyMessage?.id != interfaceState.replyMessage?.id { self.initializedPlaceholder = true var placeholder: String @@ -1810,6 +1815,12 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch } else { placeholder = interfaceState.strings.Conversation_InputTextPlaceholderReply } + } else if let channel = peer as? TelegramChannel, channel.isForum, let forumTopicData = interfaceState.forumTopicData { + if let replyMessage = interfaceState.replyMessage, let threadInfo = replyMessage.associatedThreadInfo { + placeholder = interfaceState.strings.Chat_InputPlaceholderReplyInTopic(threadInfo.title).string + } else { + placeholder = interfaceState.strings.Chat_InputPlaceholderMessageInTopic(forumTopicData.title).string + } } else { placeholder = interfaceState.strings.Conversation_InputTextPlaceholder } @@ -1824,22 +1835,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch } } } - - if self.currentPlaceholder != placeholder || themeUpdated { - self.currentPlaceholder = placeholder - let baseFontSize = max(minInputFontSize, interfaceState.fontSize.baseDisplaySize) - self.textPlaceholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(baseFontSize), textColor: interfaceState.theme.chat.inputPanel.inputPlaceholderColor) - self.textInputNode?.textView.accessibilityHint = placeholder - let placeholderSize = self.textPlaceholderNode.updateLayout(CGSize(width: 320.0, height: CGFloat.greatestFiniteMagnitude)) - if transition.isAnimated, let snapshotLayer = self.textPlaceholderNode.layer.snapshotContentTree() { - self.textPlaceholderNode.supernode?.layer.insertSublayer(snapshotLayer, above: self.textPlaceholderNode.layer) - snapshotLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.22, removeOnCompletion: false, completion: { [weak snapshotLayer] _ in - snapshotLayer?.removeFromSuperlayer() - }) - self.textPlaceholderNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18) - } - self.textPlaceholderNode.frame = CGRect(origin: self.textPlaceholderNode.frame.origin, size: placeholderSize) - } + + updatedPlaceholder = placeholder self.actionButtons.sendButtonLongPressEnabled = !isScheduledMessages } @@ -2465,9 +2462,31 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch transition.updateFrame(layer: self.textInputBackgroundNode.layer, frame: textInputBackgroundFrame) transition.updateAlpha(node: self.textInputBackgroundNode, alpha: audioRecordingItemsAlpha) + let textPlaceholderSize: CGSize + let textPlaceholderMaxWidth: CGFloat = max(1.0, (nextButtonTopRight.x - textInputBackgroundFrame.minX) - 12.0) + + if (updatedPlaceholder != nil && self.currentPlaceholder != updatedPlaceholder) || themeUpdated { + let currentPlaceholder = updatedPlaceholder ?? self.currentPlaceholder ?? "" + self.currentPlaceholder = currentPlaceholder + let baseFontSize = max(minInputFontSize, interfaceState.fontSize.baseDisplaySize) + self.textPlaceholderNode.attributedText = NSAttributedString(string: currentPlaceholder, font: Font.regular(baseFontSize), textColor: interfaceState.theme.chat.inputPanel.inputPlaceholderColor) + self.textInputNode?.textView.accessibilityHint = currentPlaceholder + let placeholderSize = self.textPlaceholderNode.updateLayout(CGSize(width: textPlaceholderMaxWidth, height: CGFloat.greatestFiniteMagnitude)) + if transition.isAnimated, let snapshotLayer = self.textPlaceholderNode.layer.snapshotContentTree() { + self.textPlaceholderNode.supernode?.layer.insertSublayer(snapshotLayer, above: self.textPlaceholderNode.layer) + snapshotLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.22, removeOnCompletion: false, completion: { [weak snapshotLayer] _ in + snapshotLayer?.removeFromSuperlayer() + }) + self.textPlaceholderNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18) + } + textPlaceholderSize = placeholderSize + } else { + textPlaceholderSize = self.textPlaceholderNode.bounds.size + } + let textPlaceholderFrame: CGRect if sendingTextDisabled { - textPlaceholderFrame = CGRect(origin: CGPoint(x: textInputBackgroundFrame.minX + floor((textInputBackgroundFrame.width - self.textPlaceholderNode.bounds.width) / 2.0), y: textFieldInsets.top + self.textInputViewInternalInsets.top + textInputViewRealInsets.top + UIScreenPixel), size: self.textPlaceholderNode.frame.size) + textPlaceholderFrame = CGRect(origin: CGPoint(x: textInputBackgroundFrame.minX + floor((textInputBackgroundFrame.width - textPlaceholderSize.width) / 2.0), y: textFieldInsets.top + self.textInputViewInternalInsets.top + textInputViewRealInsets.top + UIScreenPixel), size: textPlaceholderSize) let textLockIconNode: ASImageNode var textLockIconTransition = transition @@ -2486,7 +2505,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch textLockIconTransition.updateFrame(node: textLockIconNode, frame: CGRect(origin: CGPoint(x: -image.size.width - 4.0, y: floor((textPlaceholderFrame.height - image.size.height) / 2.0)), size: image.size)) } } else { - textPlaceholderFrame = CGRect(origin: CGPoint(x: hideOffset.x + leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: hideOffset.y + textFieldInsets.top + self.textInputViewInternalInsets.top + textInputViewRealInsets.top + UIScreenPixel), size: self.textPlaceholderNode.frame.size) + textPlaceholderFrame = CGRect(origin: CGPoint(x: hideOffset.x + leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: hideOffset.y + textFieldInsets.top + self.textInputViewInternalInsets.top + textInputViewRealInsets.top + UIScreenPixel), size: textPlaceholderSize) if let textLockIconNode = self.textLockIconNode { self.textLockIconNode = nil @@ -2495,10 +2514,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch } transition.updateFrame(node: self.textPlaceholderNode, frame: textPlaceholderFrame) - var textPlaceholderAlpha: CGFloat = audioRecordingItemsAlpha - if self.textPlaceholderNode.frame.width > (nextButtonTopRight.x - textInputBackgroundFrame.minX) - 32.0 { - textPlaceholderAlpha = 0.0 - } + let textPlaceholderAlpha: CGFloat = audioRecordingItemsAlpha transition.updateAlpha(node: self.textPlaceholderNode, alpha: textPlaceholderAlpha) if let removeAccessoryButtons = removeAccessoryButtons { diff --git a/submodules/TelegramUI/Sources/MultiScaleTextNode.swift b/submodules/TelegramUI/Sources/MultiScaleTextNode.swift deleted file mode 100644 index 00526bc9002..00000000000 --- a/submodules/TelegramUI/Sources/MultiScaleTextNode.swift +++ /dev/null @@ -1,102 +0,0 @@ -import Foundation -import UIKit -import AsyncDisplayKit -import Display - -private final class MultiScaleTextStateNode: ASDisplayNode { - let textNode: ImmediateTextNode - - var currentLayout: MultiScaleTextLayout? - - override init() { - self.textNode = ImmediateTextNode() - self.textNode.displaysAsynchronously = false - - super.init() - - self.addSubnode(self.textNode) - } -} - -final class MultiScaleTextState { - struct Attributes { - var font: UIFont - var color: UIColor - } - - let attributes: Attributes - let constrainedSize: CGSize - - init(attributes: Attributes, constrainedSize: CGSize) { - self.attributes = attributes - self.constrainedSize = constrainedSize - } -} - -struct MultiScaleTextLayout { - var size: CGSize -} - -final class MultiScaleTextNode: ASDisplayNode { - private let stateNodes: [AnyHashable: MultiScaleTextStateNode] - - init(stateKeys: [AnyHashable]) { - self.stateNodes = Dictionary(stateKeys.map { ($0, MultiScaleTextStateNode()) }, uniquingKeysWith: { lhs, _ in lhs }) - - super.init() - - for (_, node) in self.stateNodes { - self.addSubnode(node) - } - } - - func stateNode(forKey key: AnyHashable) -> ASDisplayNode? { - return self.stateNodes[key]?.textNode - } - - func updateLayout(text: String, states: [AnyHashable: MultiScaleTextState], mainState: AnyHashable) -> [AnyHashable: MultiScaleTextLayout] { - assert(Set(states.keys) == Set(self.stateNodes.keys)) - assert(states[mainState] != nil) - - var result: [AnyHashable: MultiScaleTextLayout] = [:] - var mainLayout: MultiScaleTextLayout? - for (key, state) in states { - if let node = self.stateNodes[key] { - node.textNode.attributedText = NSAttributedString(string: text, font: state.attributes.font, textColor: state.attributes.color) - node.textNode.isAccessibilityElement = true - node.textNode.accessibilityLabel = text - let nodeSize = node.textNode.updateLayout(state.constrainedSize) - let nodeLayout = MultiScaleTextLayout(size: nodeSize) - if key == mainState { - mainLayout = nodeLayout - } - node.currentLayout = nodeLayout - result[key] = nodeLayout - } - } - if let mainLayout = mainLayout { - let mainBounds = CGRect(origin: CGPoint(x: -mainLayout.size.width / 2.0, y: -mainLayout.size.height / 2.0), size: mainLayout.size) - for (key, _) in states { - if let node = self.stateNodes[key], let nodeLayout = result[key] { - node.textNode.frame = CGRect(origin: CGPoint(x: mainBounds.minX, y: mainBounds.minY + floor((mainBounds.height - nodeLayout.size.height) / 2.0)), size: nodeLayout.size) - } - } - } - return result - } - - func update(stateFractions: [AnyHashable: CGFloat], alpha: CGFloat = 1.0, transition: ContainedViewLayoutTransition) { - var fractionSum: CGFloat = 0.0 - for (_, fraction) in stateFractions { - fractionSum += fraction - } - for (key, fraction) in stateFractions { - if let node = self.stateNodes[key], let _ = node.currentLayout { - if !transition.isAnimated { - node.layer.removeAllAnimations() - } - transition.updateAlpha(node: node, alpha: fraction / fractionSum * alpha) - } - } - } -} diff --git a/submodules/TelegramUI/Sources/NavigateToChatController.swift b/submodules/TelegramUI/Sources/NavigateToChatController.swift index 7782fe6a07f..b6f83c315ee 100644 --- a/submodules/TelegramUI/Sources/NavigateToChatController.swift +++ b/submodules/TelegramUI/Sources/NavigateToChatController.swift @@ -20,96 +20,141 @@ import MediaEditorScreen import ChatControllerInteraction public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParams) { + if case let .peer(peer) = params.chatLocation { + let _ = params.context.engine.peers.ensurePeerIsLocallyAvailable(peer: peer).startStandalone() + } + + var viewForumAsMessages: Signal = .single(false) if case let .peer(peer) = params.chatLocation, case let .channel(channel) = peer, channel.flags.contains(.isForum) { - for controller in params.navigationController.viewControllers.reversed() { - var chatListController: ChatListControllerImpl? - if let controller = controller as? ChatListControllerImpl { - chatListController = controller - } else if let controller = controller as? TabBarController { - chatListController = controller.currentController as? ChatListControllerImpl + viewForumAsMessages = params.context.account.postbox.combinedView(keys: [.cachedPeerData(peerId: peer.id)]) + |> take(1) + |> map { combinedView in + guard let cachedDataView = combinedView.views[.cachedPeerData(peerId: peer.id)] as? CachedPeerDataView else { + return false } - - if let chatListController = chatListController { - var matches = false - if case let .forum(peerId) = chatListController.location, peer.id == peerId { - matches = true - } else if case let .forum(peerId) = chatListController.effectiveLocation, peer.id == peerId { - matches = true + if let cachedData = cachedDataView.cachedPeerData as? CachedChannelData, case let .known(viewForumAsMessages) = cachedData.viewForumAsMessages, viewForumAsMessages { + return true + } else { + return false + } + } + } + + let _ = (viewForumAsMessages + |> take(1) + |> deliverOnMainQueue).start(next: { viewForumAsMessages in + if case let .peer(peer) = params.chatLocation, case let .channel(channel) = peer, channel.flags.contains(.isForum), !viewForumAsMessages { + for controller in params.navigationController.viewControllers.reversed() { + var chatListController: ChatListControllerImpl? + if let controller = controller as? ChatListControllerImpl { + chatListController = controller + } else if let controller = controller as? TabBarController { + chatListController = controller.currentController as? ChatListControllerImpl } - if matches { - let _ = params.navigationController.popToViewController(controller, animated: params.animated) - if let activateMessageSearch = params.activateMessageSearch { - chatListController.activateSearch(query: activateMessageSearch.1) + if let chatListController = chatListController { + var matches = false + if case let .forum(peerId) = chatListController.location, peer.id == peerId { + matches = true + } else if case let .forum(peerId) = chatListController.effectiveLocation, peer.id == peerId { + matches = true + } + + if matches { + let _ = params.navigationController.popToViewController(controller, animated: params.animated) + if let activateMessageSearch = params.activateMessageSearch { + chatListController.activateSearch(query: activateMessageSearch.1) + } + return } - return } } + + let controller = ChatListControllerImpl(context: params.context, location: .forum(peerId: peer.id), controlsHistoryPreload: false, enableDebugActions: false) + + let activateMessageSearch = params.activateMessageSearch + params.navigationController.pushViewController(controller, completion: { [weak controller] in + guard let controller, let activateMessageSearch else { + return + } + controller.activateSearch(query: activateMessageSearch.1) + }) + + return } - let controller = ChatListControllerImpl(context: params.context, location: .forum(peerId: peer.id), controlsHistoryPreload: false, enableDebugActions: false) - - let activateMessageSearch = params.activateMessageSearch - params.navigationController.pushViewController(controller, completion: { [weak controller] in - guard let controller, let activateMessageSearch else { - return - } - controller.activateSearch(query: activateMessageSearch.1) - }) - - return - } - - var found = false - var isFirst = true - if params.useExisting { - for controller in params.navigationController.viewControllers.reversed() { - guard let controller = controller as? ChatControllerImpl else { - isFirst = false - continue - } - if controller.chatLocation.peerId == params.chatLocation.asChatLocation.peerId && controller.chatLocation.threadId == params.chatLocation.asChatLocation.threadId && (controller.subject != .scheduledMessages || controller.subject == params.subject) { - if let updateTextInputState = params.updateTextInputState { - controller.updateTextInputState(updateTextInputState) + var found = false + var isFirst = true + if params.useExisting { + for controller in params.navigationController.viewControllers.reversed() { + guard let controller = controller as? ChatControllerImpl else { + isFirst = false + continue } - var popAndComplete = true - if let subject = params.subject, case let .message(messageSubject, highlight, timecode) = subject { - if case let .id(messageId) = messageSubject { - let navigationController = params.navigationController - let animated = params.animated - controller.navigateToMessage(messageLocation: .id(messageId, NavigateToMessageParams(timestamp: timecode, quote: highlight?.quote)), animated: isFirst, completion: { [weak navigationController, weak controller] in - if let navigationController = navigationController, let controller = controller { - let _ = navigationController.popToViewController(controller, animated: animated) - } - }, customPresentProgress: { [weak navigationController] c, a in - (navigationController?.viewControllers.last as? ViewController)?.present(c, in: .window(.root), with: a) + if controller.chatLocation.peerId == params.chatLocation.asChatLocation.peerId && controller.chatLocation.threadId == params.chatLocation.asChatLocation.threadId && (controller.subject != .scheduledMessages || controller.subject == params.subject) { + if let updateTextInputState = params.updateTextInputState { + controller.updateTextInputState(updateTextInputState) + } + var popAndComplete = true + if let subject = params.subject, case let .message(messageSubject, highlight, timecode) = subject { + if case let .id(messageId) = messageSubject { + let navigationController = params.navigationController + let animated = params.animated + controller.navigateToMessage(messageLocation: .id(messageId, NavigateToMessageParams(timestamp: timecode, quote: (highlight?.quote).flatMap { quote in NavigateToMessageParams.Quote(string: quote.string, offset: quote.offset) })), animated: isFirst, completion: { [weak navigationController, weak controller] in + if let navigationController = navigationController, let controller = controller { + let _ = navigationController.popToViewController(controller, animated: animated) + } + }, customPresentProgress: { [weak navigationController] c, a in + (navigationController?.viewControllers.last as? ViewController)?.present(c, in: .window(.root), with: a) + }) + } + popAndComplete = false + } else if params.scrollToEndIfExists && isFirst { + controller.scrollToEndOfHistory() + } else if let search = params.activateMessageSearch { + controller.activateSearch(domain: search.0, query: search.1) + } else if let reportReason = params.reportReason { + controller.beginReportSelection(reason: reportReason) + } + + if popAndComplete { + if let _ = params.navigationController.viewControllers.last as? AttachmentController, let controller = params.navigationController.viewControllers[params.navigationController.viewControllers.count - 2] as? ChatControllerImpl, controller.chatLocation == params.chatLocation.asChatLocation { + + } else { + let _ = params.navigationController.popToViewController(controller, animated: params.animated) + } + params.completion(controller) + } + + controller.purposefulAction = params.purposefulAction + if let activateInput = params.activateInput { + controller.activateInput(type: activateInput) + } + if params.changeColors { + controller.presentThemeSelection() + } + if let botStart = params.botStart { + controller.updateChatPresentationInterfaceState(interactive: false, { state -> ChatPresentationInterfaceState in + return state.updatedBotStartPayload(botStart.payload) }) } - popAndComplete = false - } else if params.scrollToEndIfExists && isFirst { - controller.scrollToEndOfHistory() - } else if let search = params.activateMessageSearch { - controller.activateSearch(domain: search.0, query: search.1) - } else if let reportReason = params.reportReason { - controller.beginReportSelection(reason: reportReason) - } - - if popAndComplete { - if let _ = params.navigationController.viewControllers.last as? AttachmentController, let controller = params.navigationController.viewControllers[params.navigationController.viewControllers.count - 2] as? ChatControllerImpl, controller.chatLocation == params.chatLocation.asChatLocation { - - } else { - let _ = params.navigationController.popToViewController(controller, animated: params.animated) + if let attachBotStart = params.attachBotStart { + controller.presentAttachmentBot(botId: attachBotStart.botId, payload: attachBotStart.payload, justInstalled: attachBotStart.justInstalled) } - params.completion(controller) - } - - controller.purposefulAction = params.purposefulAction - if let activateInput = params.activateInput { - controller.activateInput(type: activateInput) - } - if params.changeColors { - controller.presentThemeSelection() + if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation { + controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload) + } + params.setupController(controller) + found = true + break } + isFirst = false + } + } + if !found { + let controller: ChatControllerImpl + if let chatController = params.chatController as? ChatControllerImpl { + controller = chatController if let botStart = params.botStart { controller.updateChatPresentationInterfaceState(interactive: false, { state -> ChatPresentationInterfaceState in return state.updatedBotStartPayload(botStart.payload) @@ -119,133 +164,112 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam controller.presentAttachmentBot(botId: attachBotStart.botId, payload: attachBotStart.payload, justInstalled: attachBotStart.justInstalled) } if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation { - controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload) + Queue.mainQueue().after(0.1) { + controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload) + } } - params.setupController(controller) - found = true - break - } - isFirst = false - } - } - if !found { - let controller: ChatControllerImpl - if let chatController = params.chatController as? ChatControllerImpl { - controller = chatController - if let botStart = params.botStart { - controller.updateChatPresentationInterfaceState(interactive: false, { state -> ChatPresentationInterfaceState in - return state.updatedBotStartPayload(botStart.payload) - }) - } - if let attachBotStart = params.attachBotStart { - controller.presentAttachmentBot(botId: attachBotStart.botId, payload: attachBotStart.payload, justInstalled: attachBotStart.justInstalled) - } - if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation { - Queue.mainQueue().after(0.1) { - controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload) + } else { + controller = ChatControllerImpl(context: params.context, chatLocation: params.chatLocation.asChatLocation, chatLocationContextHolder: params.chatLocationContextHolder, subject: params.subject, botStart: params.botStart, attachBotStart: params.attachBotStart, botAppStart: params.botAppStart, peekData: params.peekData, peerNearbyData: params.peerNearbyData, chatListFilter: params.chatListFilter, chatNavigationStack: params.chatNavigationStack) + + if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation { + Queue.mainQueue().after(0.1) { + controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload) + } } } - } else { - controller = ChatControllerImpl(context: params.context, chatLocation: params.chatLocation.asChatLocation, chatLocationContextHolder: params.chatLocationContextHolder, subject: params.subject, botStart: params.botStart, attachBotStart: params.attachBotStart, botAppStart: params.botAppStart, peekData: params.peekData, peerNearbyData: params.peerNearbyData, chatListFilter: params.chatListFilter, chatNavigationStack: params.chatNavigationStack) - - if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation { - Queue.mainQueue().after(0.1) { - controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload) - } + controller.purposefulAction = params.purposefulAction + if let search = params.activateMessageSearch { + controller.activateSearch(domain: search.0, query: search.1) } - } - controller.purposefulAction = params.purposefulAction - if let search = params.activateMessageSearch { - controller.activateSearch(domain: search.0, query: search.1) - } - let resolvedKeepStack: Bool - switch params.keepStack { - case .default: - if params.navigationController.viewControllers.contains(where: { $0 is StoryContainerScreen }) { + let resolvedKeepStack: Bool + switch params.keepStack { + case .default: + if params.navigationController.viewControllers.contains(where: { $0 is StoryContainerScreen }) { + resolvedKeepStack = true + } else { + resolvedKeepStack = params.context.sharedContext.immediateExperimentalUISettings.keepChatNavigationStack + } + case .always: resolvedKeepStack = true - } else { - resolvedKeepStack = params.context.sharedContext.immediateExperimentalUISettings.keepChatNavigationStack + case .never: + resolvedKeepStack = false } - case .always: - resolvedKeepStack = true - case .never: - resolvedKeepStack = false - } - if resolvedKeepStack { - if let pushController = params.pushController { - pushController(controller, params.animated, { - params.completion(controller) - }) - } else { - params.navigationController.pushViewController(controller, animated: params.animated, completion: { - params.completion(controller) - }) - } - } else { - let viewControllers = params.navigationController.viewControllers.filter({ controller in - if controller is ForumCreateTopicScreen { - return false + if resolvedKeepStack { + if let pushController = params.pushController { + pushController(controller, params.animated, { + params.completion(controller) + }) + } else { + params.navigationController.pushViewController(controller, animated: params.animated, completion: { + params.completion(controller) + }) } - if controller is ChatListController { - if let parentGroupId = params.parentGroupId { - return parentGroupId != .root - } else { + } else { + let viewControllers = params.navigationController.viewControllers.filter({ controller in + if controller is ForumCreateTopicScreen { + return false + } + if controller is ChatListController { + if let parentGroupId = params.parentGroupId { + return parentGroupId != .root + } else { + return true + } + } else if controller is TabBarController { return true + } else { + return false } - } else if controller is TabBarController { - return true - } else { - return false - } - }) - if viewControllers.isEmpty { - params.navigationController.replaceAllButRootController(controller, animated: params.animated, animationOptions: params.options, completion: { - params.completion(controller) }) - } else { - if params.useBackAnimation { - params.navigationController.viewControllers = [controller] + params.navigationController.viewControllers - params.navigationController.replaceControllers(controllers: viewControllers + [controller], animated: params.animated, options: params.options, completion: { + if viewControllers.isEmpty { + params.navigationController.replaceAllButRootController(controller, animated: params.animated, animationOptions: params.options, completion: { params.completion(controller) }) } else { - params.navigationController.replaceControllersAndPush(controllers: viewControllers, controller: controller, animated: params.animated, options: params.options, completion: { - params.completion(controller) - }) + if params.useBackAnimation { + params.navigationController.viewControllers = [controller] + params.navigationController.viewControllers + params.navigationController.replaceControllers(controllers: viewControllers + [controller], animated: params.animated, options: params.options, completion: { + params.completion(controller) + }) + } else { + params.navigationController.replaceControllersAndPush(controllers: viewControllers, controller: controller, animated: params.animated, options: params.options, completion: { + params.completion(controller) + }) + } } } - } - if let activateInput = params.activateInput { - controller.activateInput(type: activateInput) - } - if params.changeColors { - Queue.mainQueue().after(0.1) { - controller.presentThemeSelection() + if let activateInput = params.activateInput { + controller.activateInput(type: activateInput) + } + if params.changeColors { + Queue.mainQueue().after(0.1) { + controller.presentThemeSelection() + } } } - } - - params.navigationController.currentWindow?.forEachController { controller in - if let controller = controller as? NotificationContainerController { - controller.removeItems { item in - if let item = item as? ChatMessageNotificationItem { - for message in item.messages { - switch params.chatLocation { - case let .peer(peer): - if message.id.peerId == peer.id { - return true - } - case let .replyThread(replyThreadMessage): - if message.id.peerId == replyThreadMessage.messageId.peerId { - return true + + params.navigationController.currentWindow?.forEachController { controller in + if let controller = controller as? NotificationContainerController { + controller.removeItems { item in + if let item = item as? ChatMessageNotificationItem { + for message in item.messages { + switch params.chatLocation { + case let .peer(peer): + if message.id.peerId == peer.id { + return true + } + case let .replyThread(replyThreadMessage): + if message.id.peerId == replyThreadMessage.messageId.peerId { + return true + } } } } + return false } - return false } } - } + }) } private func findOpaqueLayer(rootLayer: CALayer, layer: CALayer) -> Bool { diff --git a/submodules/TelegramUI/Sources/Nicegram/NGDeeplinkHandler.swift b/submodules/TelegramUI/Sources/Nicegram/NGDeeplinkHandler.swift index e10eaa4ff3e..512dcc5f80f 100644 --- a/submodules/TelegramUI/Sources/Nicegram/NGDeeplinkHandler.swift +++ b/submodules/TelegramUI/Sources/Nicegram/NGDeeplinkHandler.swift @@ -10,6 +10,7 @@ import NGAnalytics import NGAssistantUI import NGAuth import NGCardUI +import NGCore import class NGCoreUI.SharedLoadingView import NGModels import NGOnboarding @@ -180,22 +181,19 @@ private extension NGDeeplinkHandler { @available(iOS 13.0, *) func handleLoginWithTelegram(url: URL) -> Bool { - let initTgLoginUseCase = AuthTgHelper.resolveInitTgLoginUseCase() - - SharedLoadingView.start() - // Retain initTgLoginUseCase - Task { + Task { @MainActor in + let initTgLoginUseCase = AuthTgHelper.resolveInitTgLoginUseCase() + + SharedLoadingView.start() + let result = await initTgLoginUseCase(source: .general) - await MainActor.run { - SharedLoadingView.stop() - - switch result { - case .success(let url): - UIApplication.shared.open(url) - case .failure(_): - break - } + SharedLoadingView.stop() + switch result { + case .success(let url): + CoreContainer.shared.urlOpener().open(url) + case .failure(_): + break } } return true diff --git a/submodules/TelegramUI/Sources/OpenChatMessage.swift b/submodules/TelegramUI/Sources/OpenChatMessage.swift index ea1bffe859d..fb5028708a5 100644 --- a/submodules/TelegramUI/Sources/OpenChatMessage.swift +++ b/submodules/TelegramUI/Sources/OpenChatMessage.swift @@ -173,7 +173,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool { }, stopLiveLocation: { messageId in params.context.liveLocationManager?.cancelLiveLocation(peerId: messageId?.peerId ?? params.message.id.peerId) }, openUrl: params.openUrl, openPeer: { peer in - params.openPeer(peer._asPeer(), .info) + params.openPeer(peer._asPeer(), .info(nil)) }, showAll: params.modal) let controller = LocationViewController(context: params.context, updatedPresentationData: params.updatedPresentationData, subject: EngineMessage(params.message), params: controllerParams) controller.navigationPresentation = .modal diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index f2f80a76a52..cba989a3384 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -41,7 +41,7 @@ private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatContr return .chat(textInputState: nil, subject: nil, peekData: nil) } } else { - return .info + return .info(nil) } } else { return navigation @@ -62,7 +62,8 @@ func openResolvedUrlImpl( present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?, - progress: Promise? + progress: Promise?, + completion: (() -> Void)? ) { let updatedPresentationData: (initial: PresentationData, signal: Signal)? if case let .chat(_, maybeUpdatedPresentationData) = urlContext { @@ -845,6 +846,7 @@ func openResolvedUrlImpl( } ) navigationController?.pushViewController(storyContainerScreen) + completion?() }) } else { var elevatedLayout = true diff --git a/submodules/TelegramUI/Sources/OpenUrl.swift b/submodules/TelegramUI/Sources/OpenUrl.swift index 64c4ddf2e12..60aa7e3d298 100644 --- a/submodules/TelegramUI/Sources/OpenUrl.swift +++ b/submodules/TelegramUI/Sources/OpenUrl.swift @@ -278,7 +278,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur context.sharedContext.applicationBindings.getWindowHost()?.present(c, on: .root, blockInteraction: false, completion: {}) }, dismissInput: { dismissInput() - }, contentContext: nil, progress: nil) + }, contentContext: nil, progress: nil, completion: nil) } } diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift index 4a656b7a493..61cddab88ee 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift @@ -36,8 +36,8 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu private let historyBackgroundNode: ASDisplayNode private let historyBackgroundContentNode: ASDisplayNode private var floatingHeaderOffset: CGFloat? - private var historyNode: ChatHistoryListNode - private var replacementHistoryNode: ChatHistoryListNode? + private var historyNode: ChatHistoryListNodeImpl + private var replacementHistoryNode: ChatHistoryListNodeImpl? private var replacementHistoryNodeFloatingOffset: CGFloat? private var validLayout: ContainerViewLayout? @@ -171,6 +171,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu }, openNoAdsDemo: { }, displayGiveawayParticipationStatus: { _ in }, openPremiumStatusInfo: { _, _, _, _ in + }, openRecommendedChannelContextMenu: { _, _, _ in }, requestMessageUpdate: { _, _ in }, cancelInteractiveKeyboardGestures: { }, dismissTextInput: { @@ -216,7 +217,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu self.isGlobalSearch = false } - self.historyNode = ChatHistoryListNode(context: context, updatedPresentationData: (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, source: source, subject: .message(id: .id(initialMessageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, reverseGroups: !self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch)) + self.historyNode = ChatHistoryListNodeImpl(context: context, updatedPresentationData: (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, source: source, subject: .message(id: .id(initialMessageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, reverseGroups: !self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch), messageTransitionNode: { return nil }) self.historyNode.clipsToBounds = true super.init() @@ -558,7 +559,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu } let chatLocationContextHolder = Atomic(value: nil) - let historyNode = ChatHistoryListNode(context: self.context, updatedPresentationData: (self.context.sharedContext.currentPresentationData.with({ $0 }), self.context.sharedContext.presentationData), chatLocation: self.chatLocation, chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, reverseGroups: !self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch)) + let historyNode = ChatHistoryListNodeImpl(context: self.context, updatedPresentationData: (self.context.sharedContext.currentPresentationData.with({ $0 }), self.context.sharedContext.presentationData), chatLocation: self.chatLocation, chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, source: .default, subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, reverseGroups: !self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch), messageTransitionNode: { return nil }) historyNode.clipsToBounds = true historyNode.preloadPages = true historyNode.stackFromBottom = true diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift deleted file mode 100644 index 40c5eca7d67..00000000000 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ /dev/null @@ -1,4136 +0,0 @@ -import Foundation -import UIKit -import AsyncDisplayKit -import Display -import Postbox -import TelegramCore -import AvatarNode -import AccountContext -import SwiftSignalKit -import TelegramPresentationData -import PhotoResources -import PeerAvatarGalleryUI -import TelegramStringFormatting -import PhoneNumberFormat -import ActivityIndicator -import TelegramUniversalVideoContent -import GalleryUI -import UniversalMediaPlayer -import RadialStatusNode -import TelegramUIPreferences -import NGData -import PeerInfoAvatarListNode -import AnimationUI -import ContextUI -import ManagedAnimationNode -import ComponentFlow -import EmojiStatusComponent -import AnimationCache -import MultiAnimationRenderer -import ComponentDisplayAdapters -import ChatTitleView -import AppBundle -import AvatarVideoNode -import PeerInfoVisualMediaPaneNode -import AvatarStoryIndicatorComponent -import ComponentDisplayAdapters -import ChatAvatarNavigationNode - -enum PeerInfoHeaderButtonKey: Hashable { - case message - case discussion - case call - case videoCall - case voiceChat - case mute - case more - case addMember - case search - case leave - case stop - case addContact -} - -enum PeerInfoHeaderButtonIcon { - case message - case call - case videoCall - case voiceChat - case mute - case unmute - case more - case addMember - case search - case leave - case stop -} - -final class PeerInfoHeaderButtonNode: HighlightableButtonNode { - let key: PeerInfoHeaderButtonKey - private let action: (PeerInfoHeaderButtonNode, ContextGesture?) -> Void - let referenceNode: ContextReferenceContentNode - let containerNode: ContextControllerSourceNode - private let backgroundNode: ASDisplayNode - private let iconNode: ASImageNode - private let textNode: ImmediateTextNode - private var animationNode: AnimationNode? - - private var theme: PresentationTheme? - private var icon: PeerInfoHeaderButtonIcon? - private var isActive: Bool? - - init(key: PeerInfoHeaderButtonKey, action: @escaping (PeerInfoHeaderButtonNode, ContextGesture?) -> Void) { - self.key = key - self.action = action - - self.referenceNode = ContextReferenceContentNode() - self.containerNode = ContextControllerSourceNode() - self.containerNode.animateScale = false - - self.backgroundNode = ASDisplayNode() - self.backgroundNode.cornerRadius = 11.0 - - self.iconNode = ASImageNode() - self.iconNode.displaysAsynchronously = false - self.iconNode.displayWithoutProcessing = true - self.iconNode.isUserInteractionEnabled = false - - self.textNode = ImmediateTextNode() - self.textNode.displaysAsynchronously = false - self.textNode.isUserInteractionEnabled = false - - super.init() - - self.accessibilityTraits = .button - - self.containerNode.addSubnode(self.referenceNode) - self.referenceNode.addSubnode(self.backgroundNode) - self.referenceNode.addSubnode(self.iconNode) - self.addSubnode(self.containerNode) - self.addSubnode(self.textNode) - - self.highligthedChanged = { [weak self] highlighted in - if let strongSelf = self { - if highlighted { - strongSelf.layer.removeAnimation(forKey: "opacity") - strongSelf.alpha = 0.4 - } else { - strongSelf.alpha = 1.0 - strongSelf.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) - } - } - } - - self.containerNode.activated = { [weak self] gesture, _ in - if let strongSelf = self { - strongSelf.action(strongSelf, gesture) - } - } - - self.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) - } - - @objc private func buttonPressed() { - switch self.icon { - case .voiceChat, .more, .leave: - self.animationNode?.playOnce() - default: - break - } - self.action(self, nil) - } - - func update(size: CGSize, text: String, icon: PeerInfoHeaderButtonIcon, isActive: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) { - let previousIcon = self.icon - let themeUpdated = self.theme != presentationData.theme - let iconUpdated = self.icon != icon - let isActiveUpdated = self.isActive != isActive - self.isActive = isActive - - let iconSize = CGSize(width: 40.0, height: 40.0) - - if themeUpdated || iconUpdated { - self.theme = presentationData.theme - self.icon = icon - - var isGestureEnabled = false - if [.mute, .voiceChat, .more].contains(icon) { - isGestureEnabled = true - } - self.containerNode.isGestureEnabled = isGestureEnabled - - let animationName: String? - var colors: [String: UIColor] = [:] - var playOnce = false - var seekToEnd = false - let iconColor = presentationData.theme.list.itemAccentColor - switch icon { - case .voiceChat: - animationName = "anim_profilevc" - colors = ["Line 3.Group 1.Stroke 1": iconColor, - "Line 1.Group 1.Stroke 1": iconColor, - "Line 2.Group 1.Stroke 1": iconColor] - case .mute: - animationName = "anim_profileunmute" - colors = ["Middle.Group 1.Fill 1": iconColor, - "Top.Group 1.Fill 1": iconColor, - "Bottom.Group 1.Fill 1": iconColor, - "EXAMPLE.Group 1.Fill 1": iconColor, - "Line.Group 1.Stroke 1": iconColor] - if previousIcon == .unmute { - playOnce = true - } else { - seekToEnd = true - } - case .unmute: - animationName = "anim_profilemute" - colors = ["Middle.Group 1.Fill 1": iconColor, - "Top.Group 1.Fill 1": iconColor, - "Bottom.Group 1.Fill 1": iconColor, - "EXAMPLE.Group 1.Fill 1": iconColor, - "Line.Group 1.Stroke 1": iconColor] - if previousIcon == .mute { - playOnce = true - } else { - seekToEnd = true - } - case .more: - animationName = "anim_profilemore" - colors = ["Point 2.Group 1.Fill 1": iconColor, - "Point 3.Group 1.Fill 1": iconColor, - "Point 1.Group 1.Fill 1": iconColor] - case .leave: - animationName = "anim_profileleave" - colors = ["Arrow.Group 2.Stroke 1": iconColor, - "Door.Group 1.Stroke 1": iconColor, - "Arrow.Group 1.Stroke 1": iconColor] - default: - animationName = nil - } - - if let animationName = animationName { - let animationNode: AnimationNode - if let current = self.animationNode { - animationNode = current - animationNode.setAnimation(name: animationName, colors: colors) - } else { - animationNode = AnimationNode(animation: animationName, colors: colors, scale: 1.0) - self.referenceNode.addSubnode(animationNode) - self.animationNode = animationNode - } - } else if let animationNode = self.animationNode { - self.animationNode = nil - animationNode.removeFromSupernode() - } - - if playOnce { - self.animationNode?.play() - } else if seekToEnd { - self.animationNode?.seekToEnd() - } - - self.backgroundNode.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor - transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size)) - self.iconNode.image = generateImage(iconSize, contextGenerator: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - context.setBlendMode(.normal) - context.setFillColor(iconColor.cgColor) - let imageName: String? - switch icon { - case .message: - imageName = "Peer Info/ButtonMessage" - case .call: - imageName = "Peer Info/ButtonCall" - case .videoCall: - imageName = "Peer Info/ButtonVideo" - case .voiceChat: - imageName = nil - case .mute: - imageName = nil - case .unmute: - imageName = nil - case .more: - imageName = nil - case .addMember: - imageName = "Peer Info/ButtonAddMember" - case .search: - imageName = "Peer Info/ButtonSearch" - case .leave: - imageName = nil - case .stop: - imageName = "Peer Info/ButtonStop" - } - if let imageName = imageName, let image = generateTintedImage(image: UIImage(bundleImageName: imageName), color: .white) { - let imageRect = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size) - context.clip(to: imageRect, mask: image.cgImage!) - context.fill(imageRect) - } - }) - } - - if isActiveUpdated { - let alphaTransition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut) - alphaTransition.updateAlpha(node: self.iconNode, alpha: isActive ? 1.0 : 0.3) - if let animationNode = self.animationNode { - alphaTransition.updateAlpha(node: animationNode, alpha: isActive ? 1.0 : 0.3) - } - alphaTransition.updateAlpha(node: self.textNode, alpha: isActive ? 1.0 : 0.3) - } - - self.textNode.attributedText = NSAttributedString(string: text.lowercased(), font: Font.regular(11.0), textColor: presentationData.theme.list.itemAccentColor) - self.accessibilityLabel = text - let titleSize = self.textNode.updateLayout(CGSize(width: 120.0, height: .greatestFiniteMagnitude)) - - transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: size)) - transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size)) - transition.updateFrame(node: self.iconNode, frame: CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: 1.0), size: iconSize)) - if let animationNode = self.animationNode { - transition.updateFrame(node: animationNode, frame: CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: 1.0), size: iconSize)) - } - transition.updateFrameAdditiveToCenter(node: self.textNode, frame: CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: size.height - titleSize.height - 9.0), size: titleSize)) - - self.referenceNode.frame = self.containerNode.bounds - } -} - -final class PeerInfoHeaderActionButtonNode: HighlightableButtonNode { - let key: PeerInfoHeaderButtonKey - private let action: (PeerInfoHeaderActionButtonNode, ContextGesture?) -> Void - let referenceNode: ContextReferenceContentNode - let containerNode: ContextControllerSourceNode - private let backgroundNode: ASDisplayNode - private let textNode: ImmediateTextNode - - private var theme: PresentationTheme? - - init(key: PeerInfoHeaderButtonKey, action: @escaping (PeerInfoHeaderActionButtonNode, ContextGesture?) -> Void) { - self.key = key - self.action = action - - self.referenceNode = ContextReferenceContentNode() - self.containerNode = ContextControllerSourceNode() - self.containerNode.animateScale = false - - self.backgroundNode = ASDisplayNode() - self.backgroundNode.cornerRadius = 11.0 - - self.textNode = ImmediateTextNode() - self.textNode.displaysAsynchronously = false - self.textNode.isUserInteractionEnabled = false - - super.init() - - self.accessibilityTraits = .button - - self.containerNode.addSubnode(self.referenceNode) - self.referenceNode.addSubnode(self.backgroundNode) - self.addSubnode(self.containerNode) - self.addSubnode(self.textNode) - - self.highligthedChanged = { [weak self] highlighted in - if let strongSelf = self { - if highlighted { - strongSelf.layer.removeAnimation(forKey: "opacity") - strongSelf.alpha = 0.4 - } else { - strongSelf.alpha = 1.0 - strongSelf.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) - } - } - } - - self.containerNode.activated = { [weak self] gesture, _ in - if let strongSelf = self { - strongSelf.action(strongSelf, gesture) - } - } - - self.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) - } - - @objc private func buttonPressed() { - self.action(self, nil) - } - - func update(size: CGSize, text: String, presentationData: PresentationData, transition: ContainedViewLayoutTransition) { - let themeUpdated = self.theme != presentationData.theme - if themeUpdated { - self.theme = presentationData.theme - - self.containerNode.isGestureEnabled = false - - self.backgroundNode.backgroundColor = presentationData.theme.list.itemAccentColor - transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size)) - } - - self.textNode.attributedText = NSAttributedString(string: text, font: Font.semibold(16.0), textColor: presentationData.theme.list.itemCheckColors.foregroundColor) - self.accessibilityLabel = text - let titleSize = self.textNode.updateLayout(CGSize(width: 120.0, height: .greatestFiniteMagnitude)) - - transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: size)) - transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size)) - transition.updateFrameAdditiveToCenter(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: floorToScreenPixels((size.height - titleSize.height) / 2.0)), size: titleSize)) - - self.referenceNode.frame = self.containerNode.bounds - } -} - -final class PeerInfoHeaderNavigationTransition { - let sourceNavigationBar: NavigationBar - let sourceTitleView: ChatTitleView - let sourceTitleFrame: CGRect - let sourceSubtitleFrame: CGRect - let previousAvatarView: UIView? - let fraction: CGFloat - - init(sourceNavigationBar: NavigationBar, sourceTitleView: ChatTitleView, sourceTitleFrame: CGRect, sourceSubtitleFrame: CGRect, previousAvatarView: UIView?, fraction: CGFloat) { - self.sourceNavigationBar = sourceNavigationBar - self.sourceTitleView = sourceTitleView - self.sourceTitleFrame = sourceTitleFrame - self.sourceSubtitleFrame = sourceSubtitleFrame - self.previousAvatarView = previousAvatarView - self.fraction = fraction - } -} - -final class PeerInfoAvatarTransformContainerNode: ASDisplayNode { - let context: AccountContext - - let containerNode: ContextControllerSourceNode - - let avatarNode: AvatarNode - private(set) var avatarStoryView: ComponentView? - fileprivate var videoNode: UniversalVideoNode? - fileprivate var markupNode: AvatarVideoNode? - fileprivate var iconView: ComponentView? - private var videoContent: NativeVideoContent? - private var videoStartTimestamp: Double? - - var isExpanded: Bool = false - var canAttachVideo: Bool = true { - didSet { - if oldValue != self.canAttachVideo { - self.videoNode?.canAttachContent = !self.isExpanded && self.canAttachVideo - } - } - } - - var tapped: (() -> Void)? - var emojiTapped: (() -> Void)? - var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? - - private var isFirstAvatarLoading = true - var item: PeerInfoAvatarListItem? - - private let playbackStartDisposable = MetaDisposable() - - var storyData: (totalCount: Int, unseenCount: Int, hasUnseenCloseFriends: Bool)? - var storyProgress: Float? - - init(context: AccountContext) { - self.context = context - self.containerNode = ContextControllerSourceNode() - - let avatarFont = avatarPlaceholderFont(size: floor(100.0 * 16.0 / 37.0)) - self.avatarNode = AvatarNode(font: avatarFont) - - super.init() - - self.addSubnode(self.containerNode) - self.containerNode.addSubnode(self.avatarNode) - self.containerNode.frame = CGRect(origin: CGPoint(x: -50.0, y: -50.0), size: CGSize(width: 100.0, height: 100.0)) - self.avatarNode.frame = self.containerNode.bounds - - let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))) - self.avatarNode.view.addGestureRecognizer(tapGestureRecognizer) - - self.containerNode.activated = { [weak self] gesture, _ in - guard let strongSelf = self else { - return - } - tapGestureRecognizer.isEnabled = false - tapGestureRecognizer.isEnabled = true - strongSelf.contextAction?(strongSelf.containerNode, gesture) - } - } - - deinit { - self.playbackStartDisposable.dispose() - } - - func updateStoryView(transition: ContainedViewLayoutTransition, theme: PresentationTheme) { - var colors = AvatarNode.Colors(theme: theme) - colors.seenColors = [ - theme.list.controlSecondaryColor, - theme.list.controlSecondaryColor - ] - var storyStats: AvatarNode.StoryStats? - if let storyData = self.storyData { - storyStats = AvatarNode.StoryStats( - totalCount: storyData.totalCount, - unseenCount: storyData.unseenCount, - hasUnseenCloseFriendsItems: storyData.hasUnseenCloseFriends, - progress: self.storyProgress - ) - } else if let storyProgress = self.storyProgress { - storyStats = AvatarNode.StoryStats( - totalCount: 1, - unseenCount: 1, - hasUnseenCloseFriendsItems: false, - progress: storyProgress - ) - } - self.avatarNode.setStoryStats(storyStats: storyStats, presentationParams: AvatarNode.StoryPresentationParams( - colors: colors, - lineWidth: 3.0, - inactiveLineWidth: 1.5 - ), transition: Transition(transition)) - } - - @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { - if case .ended = recognizer.state { - self.tapped?() - } - } - - @objc private func emojiTapGesture(_ recognizer: UITapGestureRecognizer) { - if case .ended = recognizer.state { - self.emojiTapped?() - } - } - - func updateTransitionFraction(_ fraction: CGFloat, transition: ContainedViewLayoutTransition) { - if let videoNode = self.videoNode { - if case .immediate = transition, fraction == 1.0 { - return - } - if fraction > 0.0 { - videoNode.pause() - } else { - videoNode.play() - } - transition.updateAlpha(node: videoNode, alpha: 1.0 - fraction) - } - if let markupNode = self.markupNode { - if case .immediate = transition, fraction == 1.0 { - return - } - if fraction > 0.0 { - markupNode.updateVisibility(false) - } else { - markupNode.updateVisibility(true) - } - transition.updateAlpha(node: markupNode, alpha: 1.0 - fraction) - } - } - - var removedPhotoResourceIds = Set() - func update(peer: Peer?, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool, isSettings: Bool) { - if let peer = peer { - let previousItem = self.item - var item = item - self.item = item - - var overrideImage: AvatarNodeImageOverride? - if peer.isDeleted { - overrideImage = .deletedIcon - } else if let previousItem = previousItem, item == nil { - if case let .image(_, representations, _, _, _, _) = previousItem, let rep = representations.last { - self.removedPhotoResourceIds.insert(rep.representation.resource.id.stringRepresentation) - } - overrideImage = AvatarNodeImageOverride.none - item = nil - } else if let rep = peer.profileImageRepresentations.last, self.removedPhotoResourceIds.contains(rep.resource.id.stringRepresentation) { - overrideImage = AvatarNodeImageOverride.none - item = nil - } - - if let _ = overrideImage { - self.containerNode.isGestureEnabled = false - } else if peer.profileImageRepresentations.isEmpty { - self.containerNode.isGestureEnabled = false - } else { - self.containerNode.isGestureEnabled = false - } - - self.avatarNode.imageNode.animateFirstTransition = !isSettings - self.avatarNode.setPeer(context: self.context, theme: theme, peer: EnginePeer(peer), overrideImage: overrideImage, clipStyle: .none, synchronousLoad: self.isFirstAvatarLoading, displayDimensions: CGSize(width: avatarSize, height: avatarSize), storeUnrounded: true) - - if let threadInfo = threadInfo { - self.avatarNode.isHidden = true - - let iconView: ComponentView - if let current = self.iconView { - iconView = current - } else { - iconView = ComponentView() - self.iconView = iconView - } - let content: EmojiStatusComponent.Content - if threadId == 1 { - content = .image(image: PresentationResourcesChat.chatGeneralThreadIcon(theme)) - } else if let iconFileId = threadInfo.icon { - content = .animation(content: .customEmoji(fileId: iconFileId), size: CGSize(width: avatarSize, height: avatarSize), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: theme.list.itemAccentColor, loopMode: .forever) - } else { - content = .topic(title: String(threadInfo.title.prefix(1)), color: threadInfo.iconColor, size: CGSize(width: avatarSize, height: avatarSize)) - } - let _ = iconView.update( - transition: .immediate, - component: AnyComponent(EmojiStatusComponent( - context: self.context, - animationCache: self.context.animationCache, - animationRenderer: self.context.animationRenderer, - content: content, - isVisibleForAnimations: true, - action: nil - )), - environment: {}, - containerSize: CGSize(width: avatarSize, height: avatarSize) - ) - if let iconComponentView = iconView.view { - iconComponentView.isUserInteractionEnabled = true - if iconComponentView.superview == nil { - iconComponentView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.emojiTapGesture(_:)))) - self.avatarNode.view.superview?.addSubview(iconComponentView) - } - iconComponentView.frame = CGRect(origin: CGPoint(), size: CGSize(width: avatarSize, height: avatarSize)) - } - } - - var isForum = false - let avatarCornerRadius: CGFloat - if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) { - avatarCornerRadius = floor(avatarSize * 0.25) - isForum = true - } else { - avatarCornerRadius = avatarSize / 2.0 - } - if self.avatarNode.layer.cornerRadius != 0.0 { - ContainedViewLayoutTransition.animated(duration: 0.3, curve: .easeInOut).updateCornerRadius(layer: self.avatarNode.contentNode.layer, cornerRadius: avatarCornerRadius) - } else { - self.avatarNode.contentNode.layer.cornerRadius = avatarCornerRadius - } - self.avatarNode.contentNode.layer.masksToBounds = true - - self.isFirstAvatarLoading = false - - self.containerNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize)) - self.avatarNode.frame = self.containerNode.bounds - self.avatarNode.font = avatarPlaceholderFont(size: floor(avatarSize * 16.0 / 37.0)) - - if let item = item { - let representations: [ImageRepresentationWithReference] - let videoRepresentations: [VideoRepresentationWithReference] - let immediateThumbnailData: Data? - var videoId: Int64 - let markup: TelegramMediaImage.EmojiMarkup? - switch item { - case .custom: - representations = [] - videoRepresentations = [] - immediateThumbnailData = nil - videoId = 0 - markup = nil - case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail): - representations = topRepresentations - videoRepresentations = videoRepresentationsValue - immediateThumbnailData = immediateThumbnail - videoId = peer.id.id._internalGetInt64Value() - if let resource = videoRepresentations.first?.representation.resource as? CloudPhotoSizeMediaResource { - videoId = videoId &+ resource.photoId - } - markup = nil - case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail, _, markupValue): - representations = imageRepresentations - videoRepresentations = videoRepresentationsValue - immediateThumbnailData = immediateThumbnail - if case let .cloud(imageId, _, _) = reference { - videoId = imageId - } else { - videoId = peer.id.id._internalGetInt64Value() - } - markup = markupValue - } - - self.containerNode.isGestureEnabled = !isSettings - - if let markup { - if let videoNode = self.videoNode { - self.videoContent = nil - self.videoStartTimestamp = nil - self.videoNode = nil - - videoNode.removeFromSupernode() - } - - let markupNode: AvatarVideoNode - if let current = self.markupNode { - markupNode = current - } else { - markupNode = AvatarVideoNode(context: self.context) - self.avatarNode.contentNode.addSubnode(markupNode) - self.markupNode = markupNode - } - markupNode.update(markup: markup, size: CGSize(width: 320.0, height: 320.0)) - markupNode.updateVisibility(true) - } else if threadInfo == nil, let video = videoRepresentations.last, let peerReference = PeerReference(peer) { - let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [], preloadSize: nil)])) - let videoContent = NativeVideoContent(id: .profileVideo(videoId, nil), userLocation: .other, fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: peer.isCopyProtectionEnabled, storeAfterDownload: nil) - if videoContent.id != self.videoContent?.id { - self.videoNode?.removeFromSupernode() - - let mediaManager = self.context.sharedContext.mediaManager - let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded) - videoNode.isUserInteractionEnabled = false - videoNode.isHidden = true - - if let startTimestamp = video.representation.startTimestamp { - self.videoStartTimestamp = startTimestamp - self.playbackStartDisposable.set((videoNode.status - |> map { status -> Bool in - if let status = status, case .playing = status.status { - return true - } else { - return false - } - } - |> filter { playing in - return playing - } - |> take(1) - |> deliverOnMainQueue).start(completed: { [weak self] in - if let strongSelf = self { - Queue.mainQueue().after(0.15) { - strongSelf.videoNode?.isHidden = false - } - } - })) - } else { - self.videoStartTimestamp = nil - self.playbackStartDisposable.set(nil) - videoNode.isHidden = false - } - - self.videoContent = videoContent - self.videoNode = videoNode - - let maskPath: UIBezierPath - if isForum { - maskPath = UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size), cornerRadius: avatarCornerRadius) - } else { - maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size)) - } - let shape = CAShapeLayer() - shape.path = maskPath.cgPath - videoNode.layer.mask = shape - - self.avatarNode.contentNode.addSubnode(videoNode) - } - } else { - if let markupNode = self.markupNode { - self.markupNode = nil - markupNode.removeFromSupernode() - } - if let videoNode = self.videoNode { - self.videoStartTimestamp = nil - self.videoContent = nil - self.videoNode = nil - - videoNode.removeFromSupernode() - } - } - } else { - if let markupNode = self.markupNode { - self.markupNode = nil - markupNode.removeFromSupernode() - } - if let videoNode = self.videoNode { - self.videoStartTimestamp = nil - self.videoContent = nil - self.videoNode = nil - - videoNode.removeFromSupernode() - } - self.containerNode.isGestureEnabled = false - } - - if let markupNode = self.markupNode { - markupNode.frame = self.avatarNode.bounds - markupNode.updateLayout(size: self.avatarNode.bounds.size, cornerRadius: avatarCornerRadius, transition: .immediate) - } - - if let videoNode = self.videoNode { - if self.canAttachVideo { - videoNode.updateLayout(size: self.avatarNode.frame.size, transition: .immediate) - } - videoNode.frame = self.avatarNode.contentNode.bounds - - if isExpanded == videoNode.canAttachContent { - self.isExpanded = isExpanded - let update = { - videoNode.canAttachContent = !self.isExpanded && self.canAttachVideo - if videoNode.canAttachContent { - videoNode.play() - } - } - if isExpanded { - DispatchQueue.main.async { - update() - } - } else { - update() - } - } - } - } - - self.updateStoryView(transition: .immediate, theme: theme) - } -} - -final class PeerInfoEditingAvatarOverlayNode: ASDisplayNode { - private let context: AccountContext - - private let imageNode: ImageNode - private let updatingAvatarOverlay: ASImageNode - private let iconNode: ASImageNode - private var statusNode: RadialStatusNode - - private var currentRepresentation: TelegramMediaImageRepresentation? - - init(context: AccountContext) { - self.context = context - - self.imageNode = ImageNode(enableEmpty: true) - - self.updatingAvatarOverlay = ASImageNode() - self.updatingAvatarOverlay.displayWithoutProcessing = true - self.updatingAvatarOverlay.displaysAsynchronously = false - self.updatingAvatarOverlay.alpha = 0.0 - - self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(rgb: 0x000000, alpha: 0.6)) - self.statusNode.isUserInteractionEnabled = false - - self.iconNode = ASImageNode() - self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Avatar/EditAvatarIconLarge"), color: .white) - self.iconNode.alpha = 0.0 - - super.init() - - self.imageNode.frame = CGRect(origin: CGPoint(x: -50.0, y: -50.0), size: CGSize(width: 100.0, height: 100.0)) - self.updatingAvatarOverlay.frame = self.imageNode.frame - - let radialStatusSize: CGFloat = 50.0 - let imagePosition = self.imageNode.position - self.statusNode.frame = CGRect(origin: CGPoint(x: floor(imagePosition.x - radialStatusSize / 2.0), y: floor(imagePosition.y - radialStatusSize / 2.0)), size: CGSize(width: radialStatusSize, height: radialStatusSize)) - - if let image = self.iconNode.image { - self.iconNode.frame = CGRect(origin: CGPoint(x: floor(imagePosition.x - image.size.width / 2.0), y: floor(imagePosition.y - image.size.height / 2.0)), size: image.size) - } - - self.addSubnode(self.imageNode) - self.addSubnode(self.updatingAvatarOverlay) - self.addSubnode(self.statusNode) - } - - func updateTransitionFraction(_ fraction: CGFloat, transition: ContainedViewLayoutTransition) { - transition.updateAlpha(node: self, alpha: 1.0 - fraction) - } - - func update(peer: Peer?, threadData: MessageHistoryThreadData?, chatLocation: ChatLocation, item: PeerInfoAvatarListItem?, updatingAvatar: PeerInfoUpdatingAvatar?, uploadProgress: AvatarUploadProgress?, theme: PresentationTheme, avatarSize: CGFloat, isEditing: Bool) { - guard let peer = peer else { - return - } - - self.imageNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize)) - self.updatingAvatarOverlay.frame = self.imageNode.frame - - let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .linear) - - let clipStyle: AvatarNodeClipStyle - if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) { - clipStyle = .roundedRect - } else { - clipStyle = .round - } - - var isPersonal = false - if let updatingAvatar, case let .image(image) = updatingAvatar, image.isPersonal { - isPersonal = true - } - - if canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData) - || isPersonal - || self.currentRepresentation != nil && updatingAvatar == nil { - var overlayHidden = true - if let updatingAvatar = updatingAvatar { - overlayHidden = false - - var cancelEnabled = true - let progressValue: CGFloat? - if let uploadProgress { - switch uploadProgress { - case let .value(value): - progressValue = max(0.027, value) - case .indefinite: - progressValue = nil - cancelEnabled = false - } - } else { - progressValue = 0.027 - } - self.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: progressValue, cancelEnabled: cancelEnabled, animateRotation: true)) - - if case let .image(representation) = updatingAvatar { - if representation != self.currentRepresentation { - self.currentRepresentation = representation - - if let signal = peerAvatarImage(account: context.account, peerReference: nil, authorOfMessage: nil, representation: representation, displayDimensions: CGSize(width: avatarSize, height: avatarSize), clipStyle: clipStyle, emptyColor: nil, synchronousLoad: false, provideUnrounded: false) { - self.imageNode.setSignal(signal |> map { $0?.0 }) - } - } - } - - transition.updateAlpha(node: self.updatingAvatarOverlay, alpha: 1.0) - } else { - let targetOverlayAlpha: CGFloat = 0.0 - if self.updatingAvatarOverlay.alpha != targetOverlayAlpha { - let update = { - self.statusNode.transitionToState(.none) - self.currentRepresentation = nil - self.imageNode.setSignal(.single(nil)) - transition.updateAlpha(node: self.updatingAvatarOverlay, alpha: overlayHidden ? 0.0 : 1.0) - } - Queue.mainQueue().after(0.3) { - update() - } - } - } - if !overlayHidden && self.updatingAvatarOverlay.image == nil { - switch clipStyle { - case .round: - self.updatingAvatarOverlay.image = generateFilledCircleImage(diameter: avatarSize, color: UIColor(white: 0.0, alpha: 0.4), backgroundColor: nil) - case .roundedRect: - self.updatingAvatarOverlay.image = generateFilledRoundedRectImage(size: CGSize(width: avatarSize, height: avatarSize), cornerRadius: avatarSize * 0.25, color: UIColor(white: 0.0, alpha: 0.4), backgroundColor: nil) - default: - break - } - } - } else { - self.statusNode.transitionToState(.none) - self.currentRepresentation = nil - transition.updateAlpha(node: self.iconNode, alpha: 0.0) - transition.updateAlpha(node: self.updatingAvatarOverlay, alpha: 0.0) - } - } -} - -final class PeerInfoEditingAvatarNode: ASDisplayNode { - private let context: AccountContext - let avatarNode: AvatarNode - fileprivate var videoNode: UniversalVideoNode? - fileprivate var markupNode: AvatarVideoNode? - private var videoContent: NativeVideoContent? - private var videoStartTimestamp: Double? - var item: PeerInfoAvatarListItem? - - var tapped: ((Bool) -> Void)? - - var canAttachVideo: Bool = true - - init(context: AccountContext) { - self.context = context - let avatarFont = avatarPlaceholderFont(size: floor(100.0 * 16.0 / 37.0)) - self.avatarNode = AvatarNode(font: avatarFont) - - super.init() - - self.addSubnode(self.avatarNode) - self.avatarNode.frame = CGRect(origin: CGPoint(x: -50.0, y: -50.0), size: CGSize(width: 100.0, height: 100.0)) - - self.avatarNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) - } - - @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { - if case .ended = recognizer.state { - self.tapped?(false) - } - } - - func reset() { - guard let videoNode = self.videoNode else { - return - } - videoNode.isHidden = true - videoNode.seek(self.videoStartTimestamp ?? 0.0) - Queue.mainQueue().after(0.15) { - videoNode.isHidden = false - } - } - - var removedPhotoResourceIds = Set() - func update(peer: Peer?, threadData: MessageHistoryThreadData?, chatLocation: ChatLocation, item: PeerInfoAvatarListItem?, updatingAvatar: PeerInfoUpdatingAvatar?, uploadProgress: AvatarUploadProgress?, theme: PresentationTheme, avatarSize: CGFloat, isEditing: Bool) { - guard let peer = peer else { - return - } - - let canEdit = canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData) - - let previousItem = self.item - var item = item - self.item = item - - let overrideImage: AvatarNodeImageOverride? - if canEdit, peer.profileImageRepresentations.isEmpty { - overrideImage = .editAvatarIcon(forceNone: true) - } else if let previousItem = previousItem, item == nil { - if case let .image(_, representations, _, _, _, _) = previousItem, let rep = representations.last { - self.removedPhotoResourceIds.insert(rep.representation.resource.id.stringRepresentation) - } - overrideImage = canEdit ? .editAvatarIcon(forceNone: true) : AvatarNodeImageOverride.none - item = nil - } else if let representation = peer.profileImageRepresentations.last, self.removedPhotoResourceIds.contains(representation.resource.id.stringRepresentation) { - overrideImage = canEdit ? .editAvatarIcon(forceNone: true) : AvatarNodeImageOverride.none - item = nil - } else { - overrideImage = item == nil && canEdit ? .editAvatarIcon(forceNone: true) : nil - } - self.avatarNode.font = avatarPlaceholderFont(size: floor(avatarSize * 16.0 / 37.0)) - self.avatarNode.setPeer(context: self.context, theme: theme, peer: EnginePeer(peer), overrideImage: overrideImage, clipStyle: .none, synchronousLoad: false, displayDimensions: CGSize(width: avatarSize, height: avatarSize)) - self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize)) - - var isForum = false - let avatarCornerRadius: CGFloat - if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) { - isForum = true - avatarCornerRadius = floor(avatarSize * 0.25) - } else { - avatarCornerRadius = avatarSize / 2.0 - } - if self.avatarNode.layer.cornerRadius != 0.0 { - ContainedViewLayoutTransition.animated(duration: 0.3, curve: .easeInOut).updateCornerRadius(layer: self.avatarNode.layer, cornerRadius: avatarCornerRadius) - } else { - self.avatarNode.layer.cornerRadius = avatarCornerRadius - } - self.avatarNode.layer.masksToBounds = true - - if let item = item { - let representations: [ImageRepresentationWithReference] - let videoRepresentations: [VideoRepresentationWithReference] - let immediateThumbnailData: Data? - var videoId: Int64 - let markup: TelegramMediaImage.EmojiMarkup? - switch item { - case .custom: - representations = [] - videoRepresentations = [] - immediateThumbnailData = nil - videoId = 0 - markup = nil - case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail): - representations = topRepresentations - videoRepresentations = videoRepresentationsValue - immediateThumbnailData = immediateThumbnail - videoId = peer.id.id._internalGetInt64Value() - if let resource = videoRepresentations.first?.representation.resource as? CloudPhotoSizeMediaResource { - videoId = videoId &+ resource.photoId - } - markup = nil - case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail, _, markupValue): - representations = imageRepresentations - videoRepresentations = videoRepresentationsValue - immediateThumbnailData = immediateThumbnail - if case let .cloud(imageId, _, _) = reference { - videoId = imageId - } else { - videoId = peer.id.id._internalGetInt64Value() - } - markup = markupValue - } - - if let markup { - if let videoNode = self.videoNode { - self.videoContent = nil - self.videoStartTimestamp = nil - self.videoNode = nil - - videoNode.removeFromSupernode() - } - - let markupNode: AvatarVideoNode - if let current = self.markupNode { - markupNode = current - } else { - markupNode = AvatarVideoNode(context: self.context) - self.avatarNode.contentNode.addSubnode(markupNode) - self.markupNode = markupNode - } - markupNode.update(markup: markup, size: CGSize(width: 320.0, height: 320.0)) - markupNode.updateVisibility(true) - } else if threadData == nil, let video = videoRepresentations.last, let peerReference = PeerReference(peer) { - if let markupNode = self.markupNode { - self.markupNode = nil - markupNode.removeFromSupernode() - } - - let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [], preloadSize: nil)])) - let videoContent = NativeVideoContent(id: .profileVideo(videoId, nil), userLocation: .other, fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: peer.isCopyProtectionEnabled, storeAfterDownload: nil) - if videoContent.id != self.videoContent?.id { - self.videoNode?.removeFromSupernode() - - let mediaManager = self.context.sharedContext.mediaManager - let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .gallery) - videoNode.isUserInteractionEnabled = false - self.videoStartTimestamp = video.representation.startTimestamp - self.videoContent = videoContent - self.videoNode = videoNode - - let maskPath: UIBezierPath - if isForum { - maskPath = UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size), cornerRadius: avatarCornerRadius) - } else { - maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size)) - } - let shape = CAShapeLayer() - shape.path = maskPath.cgPath - videoNode.layer.mask = shape - - self.avatarNode.contentNode.addSubnode(videoNode) - } - } else { - if let markupNode = self.markupNode { - self.markupNode = nil - markupNode.removeFromSupernode() - } - if let videoNode = self.videoNode { - self.videoStartTimestamp = nil - self.videoContent = nil - self.videoNode = nil - - videoNode.removeFromSupernode() - } - } - } else if let videoNode = self.videoNode { - self.videoStartTimestamp = nil - self.videoContent = nil - self.videoNode = nil - - videoNode.removeFromSupernode() - } - - if let markupNode = self.markupNode { - markupNode.frame = self.avatarNode.bounds - markupNode.updateLayout(size: self.avatarNode.bounds.size, cornerRadius: avatarCornerRadius, transition: .immediate) - } - - if let videoNode = self.videoNode { - if self.canAttachVideo { - videoNode.updateLayout(size: self.avatarNode.bounds.size, transition: .immediate) - } - videoNode.frame = self.avatarNode.bounds - - if isEditing != videoNode.canAttachContent { - videoNode.canAttachContent = isEditing && self.canAttachVideo - } - } - } - - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - if self.avatarNode.frame.contains(point) { - return self.avatarNode.view - } - return super.hitTest(point, with: event) - } -} - - - -final class PeerInfoAvatarListNode: ASDisplayNode { - private let isSettings: Bool - let containerNode: ASDisplayNode - let pinchSourceNode: PinchSourceContainerNode - let bottomCoverNode: ASDisplayNode - fileprivate let maskNode: DynamicIslandMaskNode - fileprivate let topCoverNode: DynamicIslandBlurNode - let avatarContainerNode: PeerInfoAvatarTransformContainerNode - let listContainerTransformNode: ASDisplayNode - let listContainerNode: PeerInfoAvatarListContainerNode - - let isReady = Promise() - - var arguments: (Peer?, Int64?, EngineMessageHistoryThread.Info?, PresentationTheme, CGFloat, Bool)? - var item: PeerInfoAvatarListItem? - - var itemsUpdated: (([PeerInfoAvatarListItem]) -> Void)? - var animateOverlaysFadeIn: (() -> Void)? - var openStories: (() -> Void)? - - init(context: AccountContext, readyWhenGalleryLoads: Bool, isSettings: Bool) { - self.isSettings = isSettings - - self.containerNode = ASDisplayNode() - - self.bottomCoverNode = ASDisplayNode() - - self.maskNode = DynamicIslandMaskNode() - self.pinchSourceNode = PinchSourceContainerNode() - - self.avatarContainerNode = PeerInfoAvatarTransformContainerNode(context: context) - self.listContainerTransformNode = ASDisplayNode() - self.listContainerNode = PeerInfoAvatarListContainerNode(context: context, isSettings: isSettings) - self.listContainerNode.clipsToBounds = true - self.listContainerNode.isHidden = true - - self.topCoverNode = DynamicIslandBlurNode() - - super.init() - - self.addSubnode(self.containerNode) - self.containerNode.addSubnode(self.bottomCoverNode) - self.containerNode.addSubnode(self.pinchSourceNode) - self.pinchSourceNode.contentNode.addSubnode(self.avatarContainerNode) - self.listContainerTransformNode.addSubnode(self.listContainerNode) - self.pinchSourceNode.contentNode.addSubnode(self.listContainerTransformNode) - self.containerNode.addSubnode(self.topCoverNode) - - let avatarReady = (self.avatarContainerNode.avatarNode.ready - |> mapToSignal { _ -> Signal in - return .complete() - } - |> then(.single(true))) - - let galleryReady = self.listContainerNode.isReady.get() - |> filter { value in - return value - } - |> take(1) - - let combinedSignal: Signal - if readyWhenGalleryLoads { - combinedSignal = combineLatest(queue: .mainQueue(), - avatarReady, - galleryReady - ) - |> map { lhs, rhs in - return lhs && rhs - } - } else { - combinedSignal = avatarReady - } - - self.isReady.set(combinedSignal - |> filter { value in - return value - } - |> take(1)) - - self.listContainerNode.itemsUpdated = { [weak self] items in - if let strongSelf = self { - strongSelf.item = items.first - strongSelf.itemsUpdated?(items) - if let (peer, threadId, threadInfo, theme, avatarSize, isExpanded) = strongSelf.arguments { - strongSelf.avatarContainerNode.update(peer: peer, threadId: threadId, threadInfo: threadInfo, item: strongSelf.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded, isSettings: strongSelf.isSettings) - } - } - } - - self.pinchSourceNode.activate = { [weak self] sourceNode in - guard let strongSelf = self, let (_, _, _, _, _, isExpanded) = strongSelf.arguments, isExpanded else { - return - } - let pinchController = PinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: { - return UIScreen.main.bounds - }) - context.sharedContext.mainWindow?.presentInGlobalOverlay(pinchController) - - strongSelf.listContainerNode.bottomShadowNode.alpha = 0.0 - } - - self.pinchSourceNode.animatedOut = { [weak self] in - guard let strongSelf = self else { - return - } - strongSelf.animateOverlaysFadeIn?() - } - - self.listContainerNode.openStories = { [weak self] in - guard let self else { - return - } - self.openStories?() - } - } - - func update(size: CGSize, avatarSize: CGFloat, isExpanded: Bool, peer: Peer?, isForum: Bool, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, theme: PresentationTheme, transition: ContainedViewLayoutTransition) { - self.arguments = (peer, threadId, threadInfo, theme, avatarSize, isExpanded) - self.maskNode.isForum = isForum - self.pinchSourceNode.update(size: size, transition: transition) - self.containerNode.frame = CGRect(origin: CGPoint(), size: size) - self.pinchSourceNode.frame = CGRect(origin: CGPoint(), size: size) - self.avatarContainerNode.update(peer: peer, threadId: threadId, threadInfo: threadInfo, item: self.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded, isSettings: self.isSettings) - } - - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - if !self.listContainerNode.isHidden { - if let result = self.listContainerNode.view.hitTest(self.view.convert(point, to: self.listContainerNode.view), with: event) { - return result - } - } else { - if let result = self.avatarContainerNode.avatarNode.view.hitTest(self.view.convert(point, to: self.avatarContainerNode.avatarNode.view), with: event) { - return result - } else if let result = self.avatarContainerNode.iconView?.view?.hitTest(self.view.convert(point, to: self.avatarContainerNode.iconView?.view), with: event) { - return result - } - } - - return super.hitTest(point, with: event) - } - - func animateAvatarCollapse(transition: ContainedViewLayoutTransition) { - if let currentItemNode = self.listContainerNode.currentItemNode, case .animated = transition { - if let _ = self.avatarContainerNode.videoNode { - - } else if let _ = self.avatarContainerNode.markupNode { - - } else if let unroundedImage = self.avatarContainerNode.avatarNode.unroundedImage { - let avatarCopyView = UIImageView() - avatarCopyView.image = unroundedImage - avatarCopyView.frame = self.avatarContainerNode.avatarNode.frame - avatarCopyView.center = currentItemNode.imageNode.position - currentItemNode.view.addSubview(avatarCopyView) - let scale = currentItemNode.imageNode.bounds.height / avatarCopyView.bounds.height - avatarCopyView.layer.transform = CATransform3DMakeScale(scale, scale, scale) - avatarCopyView.alpha = 0.0 - transition.updateAlpha(layer: avatarCopyView.layer, alpha: 1.0, completion: { [weak avatarCopyView] _ in - Queue.mainQueue().after(0.1, { - avatarCopyView?.removeFromSuperview() - }) - }) - } - } - } -} - -private enum MoreIconNodeState: Equatable { - case more - case search - case moreToSearch(Float) -} - -private final class MoreIconNode: ManagedAnimationNode { - private let duration: Double = 0.21 - private var iconState: MoreIconNodeState = .more - - init() { - super.init(size: CGSize(width: 30.0, height: 30.0)) - - self.trackTo(item: ManagedAnimationItem(source: .local("anim_moretosearch"), frames: .range(startFrame: 0, endFrame: 0), duration: 0.0)) - } - - func play() { - if case .more = self.iconState { - self.trackTo(item: ManagedAnimationItem(source: .local("anim_moredots"), frames: .range(startFrame: 0, endFrame: 46), duration: 0.76)) - } - } - - func enqueueState(_ state: MoreIconNodeState, animated: Bool) { - guard self.iconState != state else { - return - } - - let previousState = self.iconState - self.iconState = state - - let source = ManagedAnimationSource.local("anim_moretosearch") - - let totalLength: Int = 90 - if animated { - switch previousState { - case .more: - switch state { - case .more: - break - case .search: - self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: 0, endFrame: totalLength), duration: self.duration)) - case let .moreToSearch(progress): - let frame = Int(progress * Float(totalLength)) - let duration = self.duration * Double(progress) - self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: 0, endFrame: frame), duration: duration)) - } - case .search: - switch state { - case .more: - self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: totalLength, endFrame: 0), duration: self.duration)) - case .search: - break - case let .moreToSearch(progress): - let frame = Int(progress * Float(totalLength)) - let duration = self.duration * Double((1.0 - progress)) - self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: totalLength, endFrame: frame), duration: duration)) - } - case let .moreToSearch(currentProgress): - let currentFrame = Int(currentProgress * Float(totalLength)) - switch state { - case .more: - let duration = self.duration * Double(currentProgress) - self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: currentFrame, endFrame: 0), duration: duration)) - case .search: - let duration = self.duration * (1.0 - Double(currentProgress)) - self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: currentFrame, endFrame: totalLength), duration: duration)) - case let .moreToSearch(progress): - let frame = Int(progress * Float(totalLength)) - let duration = self.duration * Double(abs(currentProgress - progress)) - self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: currentFrame, endFrame: frame), duration: duration)) - } - } - } else { - switch state { - case .more: - self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: 0, endFrame: 0), duration: 0.0)) - case .search: - self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: totalLength, endFrame: totalLength), duration: 0.0)) - case let .moreToSearch(progress): - let frame = Int(progress * Float(totalLength)) - self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: frame, endFrame: frame), duration: 0.0)) - } - } - } -} - -final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { - let containerNode: ContextControllerSourceNode - let contextSourceNode: ContextReferenceContentNode - private let regularTextNode: ImmediateTextNode - private let whiteTextNode: ImmediateTextNode - private let iconNode: ASImageNode - private var animationNode: MoreIconNode? - - private var key: PeerInfoHeaderNavigationButtonKey? - private var theme: PresentationTheme? - - var isWhite: Bool = false { - didSet { - if self.isWhite != oldValue { - if case .qrCode = self.key, let theme = self.theme { - self.iconNode.image = self.isWhite ? generateTintedImage(image: PresentationResourcesRootController.navigationQrCodeIcon(theme), color: .white) : PresentationResourcesRootController.navigationQrCodeIcon(theme) - } else if case .postStory = self.key, let theme = self.theme { - self.iconNode.image = self.isWhite ? generateTintedImage(image: PresentationResourcesRootController.navigationPostStoryIcon(theme), color: .white) : PresentationResourcesRootController.navigationPostStoryIcon(theme) - } - - self.regularTextNode.isHidden = self.isWhite - self.whiteTextNode.isHidden = !self.isWhite - } - } - } - - var action: ((ASDisplayNode, ContextGesture?) -> Void)? - - init() { - self.contextSourceNode = ContextReferenceContentNode() - self.containerNode = ContextControllerSourceNode() - self.containerNode.animateScale = false - - self.regularTextNode = ImmediateTextNode() - self.whiteTextNode = ImmediateTextNode() - self.whiteTextNode.isHidden = true - - self.iconNode = ASImageNode() - self.iconNode.displaysAsynchronously = false - self.iconNode.displayWithoutProcessing = true - - super.init(pointerStyle: .insetRectangle(-8.0, 2.0)) - - self.isAccessibilityElement = true - self.accessibilityTraits = .button - - self.containerNode.addSubnode(self.contextSourceNode) - self.contextSourceNode.addSubnode(self.regularTextNode) - self.contextSourceNode.addSubnode(self.whiteTextNode) - self.contextSourceNode.addSubnode(self.iconNode) - - self.addSubnode(self.containerNode) - - self.containerNode.activated = { [weak self] gesture, _ in - guard let strongSelf = self else { - return - } - strongSelf.action?(strongSelf.contextSourceNode, gesture) - } - - self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside) - } - - @objc private func pressed() { - self.animationNode?.play() - self.action?(self.contextSourceNode, nil) - } - - func update(key: PeerInfoHeaderNavigationButtonKey, presentationData: PresentationData, height: CGFloat) -> CGSize { - let textSize: CGSize - let isFirstTime = self.key == nil - if self.key != key || self.theme !== presentationData.theme { - self.key = key - self.theme = presentationData.theme - - let text: String - var accessibilityText: String - var icon: UIImage? - var isBold = false - var isGestureEnabled = false - var isAnimation = false - var animationState: MoreIconNodeState = .more - switch key { - case .edit: - text = presentationData.strings.Common_Edit - accessibilityText = text - case .done, .cancel, .selectionDone: - text = presentationData.strings.Common_Done - accessibilityText = text - isBold = true - case .select: - text = presentationData.strings.Common_Select - accessibilityText = text - case .search: - text = "" - accessibilityText = presentationData.strings.Common_Search - icon = nil// PresentationResourcesRootController.navigationCompactSearchIcon(presentationData.theme) - isAnimation = true - animationState = .search - case .editPhoto: - text = presentationData.strings.Settings_EditPhoto - accessibilityText = text - case .editVideo: - text = presentationData.strings.Settings_EditVideo - accessibilityText = text - case .more: - text = "" - accessibilityText = presentationData.strings.Common_More - icon = nil// PresentationResourcesRootController.navigationMoreCircledIcon(presentationData.theme) - isGestureEnabled = true - isAnimation = true - animationState = .more - case .qrCode: - text = "" - accessibilityText = presentationData.strings.PeerInfo_QRCode_Title - icon = PresentationResourcesRootController.navigationQrCodeIcon(presentationData.theme) - case .moreToSearch: - text = "" - accessibilityText = "" - case .postStory: - text = "" - accessibilityText = presentationData.strings.Story_Privacy_PostStory - icon = PresentationResourcesRootController.navigationPostStoryIcon(presentationData.theme) - } - self.accessibilityLabel = accessibilityText - self.containerNode.isGestureEnabled = isGestureEnabled - - let font: UIFont = isBold ? Font.semibold(17.0) : Font.regular(17.0) - - self.regularTextNode.attributedText = NSAttributedString(string: text, font: font, textColor: presentationData.theme.rootController.navigationBar.accentTextColor) - self.whiteTextNode.attributedText = NSAttributedString(string: text, font: font, textColor: .white) - self.iconNode.image = icon - - if isAnimation { - self.iconNode.isHidden = true - let animationNode: MoreIconNode - if let current = self.animationNode { - animationNode = current - } else { - animationNode = MoreIconNode() - self.animationNode = animationNode - self.contextSourceNode.addSubnode(animationNode) - } - animationNode.customColor = presentationData.theme.rootController.navigationBar.accentTextColor - animationNode.enqueueState(animationState, animated: !isFirstTime) - } else { - self.iconNode.isHidden = false - if let current = self.animationNode { - self.animationNode = nil - current.removeFromSupernode() - } - } - - textSize = self.regularTextNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude)) - let _ = self.whiteTextNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude)) - } else { - textSize = self.regularTextNode.bounds.size - } - - let inset: CGFloat = 0.0 - - let textFrame = CGRect(origin: CGPoint(x: inset, y: floor((height - textSize.height) / 2.0)), size: textSize) - self.regularTextNode.frame = textFrame - self.whiteTextNode.frame = textFrame - - if let animationNode = self.animationNode { - let animationSize = CGSize(width: 30.0, height: 30.0) - - animationNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((height - animationSize.height) / 2.0)), size: animationSize) - - let size = CGSize(width: animationSize.width + inset * 2.0, height: height) - self.containerNode.frame = CGRect(origin: CGPoint(), size: size) - self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: size) - return size - } else if let image = self.iconNode.image { - self.iconNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((height - image.size.height) / 2.0)), size: image.size) - - let size = CGSize(width: image.size.width + inset * 2.0, height: height) - self.containerNode.frame = CGRect(origin: CGPoint(), size: size) - self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: size) - return size - } else { - let size = CGSize(width: textSize.width + inset * 2.0, height: height) - self.containerNode.frame = CGRect(origin: CGPoint(), size: size) - self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: size) - return size - } - } -} - -enum PeerInfoHeaderNavigationButtonKey { - case edit - case done - case cancel - case select - case selectionDone - case search - case editPhoto - case editVideo - case more - case qrCode - case moreToSearch - case postStory -} - -struct PeerInfoHeaderNavigationButtonSpec: Equatable { - let key: PeerInfoHeaderNavigationButtonKey - let isForExpandedView: Bool -} - -final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { - private var presentationData: PresentationData? - private(set) var leftButtonNodes: [PeerInfoHeaderNavigationButtonKey: PeerInfoHeaderNavigationButton] = [:] - private(set) var rightButtonNodes: [PeerInfoHeaderNavigationButtonKey: PeerInfoHeaderNavigationButton] = [:] - - private var currentLeftButtons: [PeerInfoHeaderNavigationButtonSpec] = [] - private var currentRightButtons: [PeerInfoHeaderNavigationButtonSpec] = [] - - var isWhite: Bool = false { - didSet { - if self.isWhite != oldValue { - for (_, buttonNode) in self.leftButtonNodes { - buttonNode.isWhite = self.isWhite - } - for (_, buttonNode) in self.rightButtonNodes { - buttonNode.isWhite = self.isWhite - } - } - } - } - - var performAction: ((PeerInfoHeaderNavigationButtonKey, ContextReferenceContentNode?, ContextGesture?) -> Void)? - - func update(size: CGSize, presentationData: PresentationData, leftButtons: [PeerInfoHeaderNavigationButtonSpec], rightButtons: [PeerInfoHeaderNavigationButtonSpec], expandFraction: CGFloat, transition: ContainedViewLayoutTransition) { - let maximumExpandOffset: CGFloat = 14.0 - let expandOffset: CGFloat = -expandFraction * maximumExpandOffset - - if self.currentLeftButtons != leftButtons || presentationData.strings !== self.presentationData?.strings { - self.currentLeftButtons = leftButtons - - var nextRegularButtonOrigin = 16.0 - var nextExpandedButtonOrigin = 16.0 - for spec in leftButtons.reversed() { - let buttonNode: PeerInfoHeaderNavigationButton - var wasAdded = false - if let current = self.leftButtonNodes[spec.key] { - buttonNode = current - } else { - wasAdded = true - buttonNode = PeerInfoHeaderNavigationButton() - self.leftButtonNodes[spec.key] = buttonNode - self.addSubnode(buttonNode) - buttonNode.action = { [weak self] _, gesture in - guard let strongSelf = self, let buttonNode = strongSelf.leftButtonNodes[spec.key] else { - return - } - strongSelf.performAction?(spec.key, buttonNode.contextSourceNode, gesture) - } - } - let buttonSize = buttonNode.update(key: spec.key, presentationData: presentationData, height: size.height) - var nextButtonOrigin = spec.isForExpandedView ? nextExpandedButtonOrigin : nextRegularButtonOrigin - let buttonFrame = CGRect(origin: CGPoint(x: nextButtonOrigin, y: expandOffset + (spec.isForExpandedView ? maximumExpandOffset : 0.0)), size: buttonSize) - nextButtonOrigin += buttonSize.width + 4.0 - if spec.isForExpandedView { - nextExpandedButtonOrigin = nextButtonOrigin - } else { - nextRegularButtonOrigin = nextButtonOrigin - } - let alphaFactor: CGFloat = spec.isForExpandedView ? expandFraction : (1.0 - expandFraction) - if wasAdded { - buttonNode.frame = buttonFrame - buttonNode.alpha = 0.0 - transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) - - buttonNode.isWhite = self.isWhite - } else { - transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) - transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) - } - } - var removeKeys: [PeerInfoHeaderNavigationButtonKey] = [] - for (key, _) in self.leftButtonNodes { - if !leftButtons.contains(where: { $0.key == key }) { - removeKeys.append(key) - } - } - for key in removeKeys { - if let buttonNode = self.leftButtonNodes.removeValue(forKey: key) { - buttonNode.removeFromSupernode() - } - } - } else { - var nextRegularButtonOrigin = 16.0 - var nextExpandedButtonOrigin = 16.0 - for spec in leftButtons.reversed() { - if let buttonNode = self.leftButtonNodes[spec.key] { - let buttonSize = buttonNode.bounds.size - var nextButtonOrigin = spec.isForExpandedView ? nextExpandedButtonOrigin : nextRegularButtonOrigin - let buttonFrame = CGRect(origin: CGPoint(x: nextButtonOrigin, y: expandOffset + (spec.isForExpandedView ? maximumExpandOffset : 0.0)), size: buttonSize) - nextButtonOrigin += buttonSize.width + 4.0 - if spec.isForExpandedView { - nextExpandedButtonOrigin = nextButtonOrigin - } else { - nextRegularButtonOrigin = nextButtonOrigin - } - transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) - let alphaFactor: CGFloat = spec.isForExpandedView ? expandFraction : (1.0 - expandFraction) - - var buttonTransition = transition - if case let .animated(duration, curve) = buttonTransition, alphaFactor == 0.0 { - buttonTransition = .animated(duration: duration * 0.25, curve: curve) - } - buttonTransition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) - } - } - } - - if self.currentRightButtons != rightButtons || presentationData.strings !== self.presentationData?.strings { - self.currentRightButtons = rightButtons - - var nextRegularButtonOrigin = size.width - 16.0 - var nextExpandedButtonOrigin = size.width - 16.0 - for spec in rightButtons.reversed() { - let buttonNode: PeerInfoHeaderNavigationButton - var wasAdded = false - - var key = spec.key - if key == .more || key == .search { - key = .moreToSearch - } - - if let current = self.rightButtonNodes[key] { - buttonNode = current - } else { - wasAdded = true - buttonNode = PeerInfoHeaderNavigationButton() - self.rightButtonNodes[key] = buttonNode - self.addSubnode(buttonNode) - } - buttonNode.action = { [weak self] _, gesture in - guard let strongSelf = self, let buttonNode = strongSelf.rightButtonNodes[key] else { - return - } - strongSelf.performAction?(spec.key, buttonNode.contextSourceNode, gesture) - } - let buttonSize = buttonNode.update(key: spec.key, presentationData: presentationData, height: size.height) - var nextButtonOrigin = spec.isForExpandedView ? nextExpandedButtonOrigin : nextRegularButtonOrigin - var buttonFrame = CGRect(origin: CGPoint(x: nextButtonOrigin - buttonSize.width, y: expandOffset + (spec.isForExpandedView ? maximumExpandOffset : 0.0)), size: buttonSize) - if case .postStory = spec.key { - buttonFrame.origin.x -= 12.0 - } - nextButtonOrigin -= buttonSize.width + 4.0 - if spec.isForExpandedView { - nextExpandedButtonOrigin = nextButtonOrigin - } else { - nextRegularButtonOrigin = nextButtonOrigin - } - let alphaFactor: CGFloat = spec.isForExpandedView ? expandFraction : (1.0 - expandFraction) - if wasAdded { - buttonNode.isWhite = self.isWhite - - if key == .moreToSearch { - buttonNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2) - } - - buttonNode.frame = buttonFrame - buttonNode.alpha = 0.0 - transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) - } else { - transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) - transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) - } - } - var removeKeys: [PeerInfoHeaderNavigationButtonKey] = [] - for (key, _) in self.rightButtonNodes { - if key == .moreToSearch { - if !rightButtons.contains(where: { $0.key == .more || $0.key == .search }) { - removeKeys.append(key) - } - } else if !rightButtons.contains(where: { $0.key == key }) { - removeKeys.append(key) - } - } - for key in removeKeys { - if let buttonNode = self.rightButtonNodes.removeValue(forKey: key) { - if key == .moreToSearch { - buttonNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak buttonNode] _ in - buttonNode?.removeFromSupernode() - }) - buttonNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false) - } else { - buttonNode.removeFromSupernode() - } - } - } - } else { - var nextRegularButtonOrigin = size.width - 16.0 - var nextExpandedButtonOrigin = size.width - 16.0 - - for spec in rightButtons.reversed() { - var key = spec.key - if key == .more || key == .search { - key = .moreToSearch - } - - if let buttonNode = self.rightButtonNodes[key] { - let buttonSize = buttonNode.bounds.size - var nextButtonOrigin = spec.isForExpandedView ? nextExpandedButtonOrigin : nextRegularButtonOrigin - var buttonFrame = CGRect(origin: CGPoint(x: nextButtonOrigin - buttonSize.width, y: expandOffset + (spec.isForExpandedView ? maximumExpandOffset : 0.0)), size: buttonSize) - if case .postStory = spec.key { - buttonFrame.origin.x -= 12.0 - } - nextButtonOrigin -= buttonSize.width + 4.0 - if spec.isForExpandedView { - nextExpandedButtonOrigin = nextButtonOrigin - } else { - nextRegularButtonOrigin = nextButtonOrigin - } - transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) - let alphaFactor: CGFloat = spec.isForExpandedView ? expandFraction : (1.0 - expandFraction) - - var buttonTransition = transition - if case let .animated(duration, curve) = buttonTransition, alphaFactor == 0.0 { - buttonTransition = .animated(duration: duration * 0.25, curve: curve) - } - buttonTransition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) - } - } - } - self.presentationData = presentationData - } -} - -final class PeerInfoHeaderRegularContentNode: ASDisplayNode { - -} - -enum PeerInfoHeaderTextFieldNodeKey: Equatable { - case firstName - case lastName - case title - case description -} - -protocol PeerInfoHeaderTextFieldNode: ASDisplayNode { - var text: String { get } - - func update(width: CGFloat, safeInset: CGFloat, isSettings: Bool, hasPrevious: Bool, hasNext: Bool, placeholder: String, isEnabled: Bool, presentationData: PresentationData, updateText: String?) -> CGFloat -} - -final class PeerInfoHeaderSingleLineTextFieldNode: ASDisplayNode, PeerInfoHeaderTextFieldNode, UITextFieldDelegate { - private let backgroundNode: ASDisplayNode - private let textNode: TextFieldNode - private let measureTextNode: ImmediateTextNode - private let clearIconNode: ASImageNode - private let clearButtonNode: HighlightableButtonNode - private let topSeparator: ASDisplayNode - private let maskNode: ASImageNode - - private var theme: PresentationTheme? - - var text: String { - return self.textNode.textField.text ?? "" - } - - override init() { - self.backgroundNode = ASDisplayNode() - - self.textNode = TextFieldNode() - self.measureTextNode = ImmediateTextNode() - self.measureTextNode.maximumNumberOfLines = 0 - - self.clearIconNode = ASImageNode() - self.clearIconNode.isLayerBacked = true - self.clearIconNode.displayWithoutProcessing = true - self.clearIconNode.displaysAsynchronously = false - self.clearIconNode.isHidden = true - - self.clearButtonNode = HighlightableButtonNode() - self.clearButtonNode.isHidden = true - self.clearButtonNode.isAccessibilityElement = false - - self.topSeparator = ASDisplayNode() - - self.maskNode = ASImageNode() - self.maskNode.isUserInteractionEnabled = false - - super.init() - - self.addSubnode(self.backgroundNode) - self.addSubnode(self.textNode) - self.addSubnode(self.clearIconNode) - self.addSubnode(self.clearButtonNode) - self.addSubnode(self.topSeparator) - self.addSubnode(self.maskNode) - - self.textNode.textField.delegate = self - - self.clearButtonNode.addTarget(self, action: #selector(self.clearButtonPressed), forControlEvents: .touchUpInside) - self.clearButtonNode.highligthedChanged = { [weak self] highlighted in - if let strongSelf = self { - if highlighted { - strongSelf.clearIconNode.layer.removeAnimation(forKey: "opacity") - strongSelf.clearIconNode.alpha = 0.4 - } else { - strongSelf.clearIconNode.alpha = 1.0 - strongSelf.clearIconNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) - } - } - } - } - - @objc private func clearButtonPressed() { - self.textNode.textField.text = "" - self.updateClearButtonVisibility() - } - - @objc func textFieldDidBeginEditing(_ textField: UITextField) { - self.updateClearButtonVisibility() - } - - @objc func textFieldDidEndEditing(_ textField: UITextField) { - self.updateClearButtonVisibility() - } - - private func updateClearButtonVisibility() { - let isHidden = !self.textNode.textField.isFirstResponder || self.text.isEmpty - self.clearIconNode.isHidden = isHidden - self.clearButtonNode.isHidden = isHidden - self.clearButtonNode.isAccessibilityElement = isHidden - } - - func update(width: CGFloat, safeInset: CGFloat, isSettings: Bool, hasPrevious: Bool, hasNext: Bool, placeholder: String, isEnabled: Bool, presentationData: PresentationData, updateText: String?) -> CGFloat { - let titleFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) - self.textNode.textField.font = titleFont - - if self.theme !== presentationData.theme { - self.theme = presentationData.theme - - self.backgroundNode.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor - - self.textNode.textField.textColor = presentationData.theme.list.itemPrimaryTextColor - self.textNode.textField.keyboardAppearance = presentationData.theme.rootController.keyboardColor.keyboardAppearance - self.textNode.textField.tintColor = presentationData.theme.list.itemAccentColor - - self.clearIconNode.image = PresentationResourcesItemList.itemListClearInputIcon(presentationData.theme) - } - - let attributedPlaceholderText = NSAttributedString(string: placeholder, font: titleFont, textColor: presentationData.theme.list.itemPlaceholderTextColor) - if self.textNode.textField.attributedPlaceholder == nil || !self.textNode.textField.attributedPlaceholder!.isEqual(to: attributedPlaceholderText) { - self.textNode.textField.attributedPlaceholder = attributedPlaceholderText - self.textNode.textField.accessibilityHint = attributedPlaceholderText.string - } - - if let updateText = updateText { - self.textNode.textField.text = updateText - } - - if !hasPrevious { - self.topSeparator.isHidden = true - } - self.topSeparator.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor - let separatorX = safeInset + (hasPrevious ? 16.0 : 0.0) - self.topSeparator.frame = CGRect(origin: CGPoint(x: separatorX, y: 0.0), size: CGSize(width: width - separatorX - safeInset, height: UIScreenPixel)) - - let measureText = "|" - let attributedMeasureText = NSAttributedString(string: measureText, font: titleFont, textColor: .black) - self.measureTextNode.attributedText = attributedMeasureText - let measureTextSize = self.measureTextNode.updateLayout(CGSize(width: width - safeInset * 2.0 - 16.0 * 2.0 - 38.0, height: .greatestFiniteMagnitude)) - - let height = measureTextSize.height + 22.0 - - let buttonSize = CGSize(width: 38.0, height: height) - self.clearButtonNode.frame = CGRect(origin: CGPoint(x: width - safeInset - buttonSize.width, y: 0.0), size: buttonSize) - if let image = self.clearIconNode.image { - self.clearIconNode.frame = CGRect(origin: CGPoint(x: width - safeInset - buttonSize.width + floor((buttonSize.width - image.size.width) / 2.0), y: floor((height - image.size.height) / 2.0)), size: image.size) - } - - self.backgroundNode.frame = CGRect(origin: CGPoint(x: safeInset, y: 0.0), size: CGSize(width: max(1.0, width - safeInset * 2.0), height: height)) - self.textNode.frame = CGRect(origin: CGPoint(x: safeInset + 16.0, y: floor((height - 40.0) / 2.0)), size: CGSize(width: max(1.0, width - safeInset * 2.0 - 16.0 * 2.0 - 38.0), height: 40.0)) - - let hasCorners = safeInset > 0.0 && (!hasPrevious || !hasNext) - let hasTopCorners = hasCorners && !hasPrevious - let hasBottomCorners = hasCorners && !hasNext - - self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil - self.maskNode.frame = CGRect(origin: CGPoint(x: safeInset, y: 0.0), size: CGSize(width: width - safeInset - safeInset, height: height)) - - self.textNode.isUserInteractionEnabled = isEnabled - self.textNode.alpha = isEnabled ? 1.0 : 0.6 - - return height - } -} - -final class PeerInfoHeaderMultiLineTextFieldNode: ASDisplayNode, PeerInfoHeaderTextFieldNode, ASEditableTextNodeDelegate { - private let backgroundNode: ASDisplayNode - private let textNode: EditableTextNode - private let textNodeContainer: ASDisplayNode - private let measureTextNode: ImmediateTextNode - private let clearIconNode: ASImageNode - private let clearButtonNode: HighlightableButtonNode - private let topSeparator: ASDisplayNode - private let maskNode: ASImageNode - - private let requestUpdateHeight: () -> Void - - private var fontSize: PresentationFontSize? - private var theme: PresentationTheme? - private var currentParams: (width: CGFloat, safeInset: CGFloat)? - private var currentMeasuredHeight: CGFloat? - - var text: String { - return self.textNode.attributedText?.string ?? "" - } - - init(requestUpdateHeight: @escaping () -> Void) { - self.requestUpdateHeight = requestUpdateHeight - - self.backgroundNode = ASDisplayNode() - - self.textNode = EditableTextNode() - self.textNode.clipsToBounds = false - self.textNode.textView.clipsToBounds = false - self.textNode.textContainerInset = UIEdgeInsets() - - self.textNodeContainer = ASDisplayNode() - self.measureTextNode = ImmediateTextNode() - self.measureTextNode.maximumNumberOfLines = 0 - self.measureTextNode.isUserInteractionEnabled = false - self.measureTextNode.lineSpacing = 0.1 - self.topSeparator = ASDisplayNode() - - self.clearIconNode = ASImageNode() - self.clearIconNode.isLayerBacked = true - self.clearIconNode.displayWithoutProcessing = true - self.clearIconNode.displaysAsynchronously = false - self.clearIconNode.isHidden = true - - self.clearButtonNode = HighlightableButtonNode() - self.clearButtonNode.isHidden = true - self.clearButtonNode.isAccessibilityElement = false - - self.maskNode = ASImageNode() - self.maskNode.isUserInteractionEnabled = false - - super.init() - - self.addSubnode(self.backgroundNode) - self.textNodeContainer.addSubnode(self.textNode) - self.addSubnode(self.textNodeContainer) - self.addSubnode(self.clearIconNode) - self.addSubnode(self.clearButtonNode) - self.addSubnode(self.topSeparator) - self.addSubnode(self.maskNode) - - self.clearButtonNode.addTarget(self, action: #selector(self.clearButtonPressed), forControlEvents: .touchUpInside) - self.clearButtonNode.highligthedChanged = { [weak self] highlighted in - if let strongSelf = self { - if highlighted { - strongSelf.clearIconNode.layer.removeAnimation(forKey: "opacity") - strongSelf.clearIconNode.alpha = 0.4 - } else { - strongSelf.clearIconNode.alpha = 1.0 - strongSelf.clearIconNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) - } - } - } - } - - @objc private func clearButtonPressed() { - guard let theme = self.theme else { - return - } - let font: UIFont - if let fontSize = self.fontSize { - font = Font.regular(fontSize.itemListBaseFontSize) - } else { - font = Font.regular(17.0) - } - let attributedText = NSAttributedString(string: "", font: font, textColor: theme.list.itemPrimaryTextColor) - self.textNode.attributedText = attributedText - self.requestUpdateHeight() - self.updateClearButtonVisibility() - } - - func update(width: CGFloat, safeInset: CGFloat, isSettings: Bool, hasPrevious: Bool, hasNext: Bool, placeholder: String, isEnabled: Bool, presentationData: PresentationData, updateText: String?) -> CGFloat { - self.currentParams = (width, safeInset) - - self.fontSize = presentationData.listsFontSize - let titleFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) - - if self.theme !== presentationData.theme { - self.theme = presentationData.theme - - self.backgroundNode.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor - - let textColor = presentationData.theme.list.itemPrimaryTextColor - self.textNode.typingAttributes = [NSAttributedString.Key.font.rawValue: titleFont, NSAttributedString.Key.foregroundColor.rawValue: textColor] - self.textNode.keyboardAppearance = presentationData.theme.rootController.keyboardColor.keyboardAppearance - self.textNode.tintColor = presentationData.theme.list.itemAccentColor - - self.textNode.clipsToBounds = true - self.textNode.delegate = self - self.textNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) - - self.clearIconNode.image = PresentationResourcesItemList.itemListClearInputIcon(presentationData.theme) - } - - self.topSeparator.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor - - let separatorX = safeInset + (hasPrevious ? 16.0 : 0.0) - self.topSeparator.frame = CGRect(origin: CGPoint(x: separatorX, y: 0.0), size: CGSize(width: width - separatorX - safeInset, height: UIScreenPixel)) - - let attributedPlaceholderText = NSAttributedString(string: placeholder, font: titleFont, textColor: presentationData.theme.list.itemPlaceholderTextColor) - if self.textNode.attributedPlaceholderText == nil || !self.textNode.attributedPlaceholderText!.isEqual(to: attributedPlaceholderText) { - self.textNode.attributedPlaceholderText = attributedPlaceholderText - } - - if let updateText = updateText { - let attributedText = NSAttributedString(string: updateText, font: titleFont, textColor: presentationData.theme.list.itemPrimaryTextColor) - self.textNode.attributedText = attributedText - } - - var measureText = self.textNode.attributedText?.string ?? "" - if measureText.hasSuffix("\n") || measureText.isEmpty { - measureText += "|" - } - let attributedMeasureText = NSAttributedString(string: measureText, font: titleFont, textColor: .gray) - self.measureTextNode.attributedText = attributedMeasureText - let measureTextSize = self.measureTextNode.updateLayout(CGSize(width: width - safeInset * 2.0 - 16.0 * 2.0 - 38.0, height: .greatestFiniteMagnitude)) - self.measureTextNode.frame = CGRect(origin: CGPoint(), size: measureTextSize) - self.currentMeasuredHeight = measureTextSize.height - - let height = measureTextSize.height + 22.0 - - let buttonSize = CGSize(width: 38.0, height: height) - self.clearButtonNode.frame = CGRect(origin: CGPoint(x: width - safeInset - buttonSize.width, y: 0.0), size: buttonSize) - if let image = self.clearIconNode.image { - self.clearIconNode.frame = CGRect(origin: CGPoint(x: width - safeInset - buttonSize.width + floor((buttonSize.width - image.size.width) / 2.0), y: floor((height - image.size.height) / 2.0)), size: image.size) - } - - let textNodeFrame = CGRect(origin: CGPoint(x: safeInset + 16.0, y: 10.0), size: CGSize(width: width - safeInset * 2.0 - 16.0 * 2.0 - 38.0, height: max(height, 1000.0))) - self.textNodeContainer.frame = textNodeFrame - self.textNode.frame = CGRect(origin: CGPoint(), size: textNodeFrame.size) - - self.backgroundNode.frame = CGRect(origin: CGPoint(x: safeInset, y: 0.0), size: CGSize(width: max(1.0, width - safeInset * 2.0), height: height)) - - let hasCorners = safeInset > 0.0 && (!hasPrevious || !hasNext) - let hasTopCorners = hasCorners && !hasPrevious - let hasBottomCorners = hasCorners && !hasNext - - self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil - self.maskNode.frame = CGRect(origin: CGPoint(x: safeInset, y: 0.0), size: CGSize(width: width - safeInset - safeInset, height: height)) - - return height - } - - func editableTextNodeDidBeginEditing(_ editableTextNode: ASEditableTextNode) { - self.updateClearButtonVisibility() - } - - func editableTextNodeDidFinishEditing(_ editableTextNode: ASEditableTextNode) { - self.updateClearButtonVisibility() - } - - private func updateClearButtonVisibility() { - let isHidden = !self.textNode.isFirstResponder() || self.text.isEmpty - self.clearIconNode.isHidden = isHidden - self.clearButtonNode.isHidden = isHidden - self.clearButtonNode.isAccessibilityElement = isHidden - } - - func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { - guard let theme = self.theme else { - return true - } - let updatedText = (editableTextNode.textView.text as NSString).replacingCharacters(in: range, with: text) - if updatedText.count > 255 { - let attributedText = NSAttributedString(string: String(updatedText[updatedText.startIndex.. 0.1 { - self.requestUpdateHeight() - } - } - } - - func editableTextNodeShouldPaste(_ editableTextNode: ASEditableTextNode) -> Bool { - let text: String? = UIPasteboard.general.string - if let _ = text { - return true - } else { - return false - } - } -} - -final class PeerInfoHeaderEditingContentNode: ASDisplayNode { - private let context: AccountContext - private let requestUpdateLayout: () -> Void - - var requestEditing: (() -> Void)? - - let avatarNode: PeerInfoEditingAvatarNode - let avatarTextNode: ImmediateTextNode - let avatarButtonNode: HighlightableButtonNode - - var itemNodes: [PeerInfoHeaderTextFieldNodeKey: PeerInfoHeaderTextFieldNode] = [:] - - init(context: AccountContext, requestUpdateLayout: @escaping () -> Void) { - self.context = context - self.requestUpdateLayout = requestUpdateLayout - - self.avatarNode = PeerInfoEditingAvatarNode(context: context) - - self.avatarTextNode = ImmediateTextNode() - self.avatarButtonNode = HighlightableButtonNode() - - super.init() - - self.addSubnode(self.avatarNode) - self.avatarButtonNode.addSubnode(self.avatarTextNode) - - self.avatarButtonNode.addTarget(self, action: #selector(textPressed), forControlEvents: .touchUpInside) - } - - @objc private func textPressed() { - self.requestEditing?() - } - - func editingTextForKey(_ key: PeerInfoHeaderTextFieldNodeKey) -> String? { - return self.itemNodes[key]?.text - } - - func shakeTextForKey(_ key: PeerInfoHeaderTextFieldNodeKey) { - self.itemNodes[key]?.layer.addShakeAnimation() - } - - func update(width: CGFloat, safeInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, peer: Peer?, threadData: MessageHistoryThreadData?, chatLocation: ChatLocation, cachedData: CachedPeerData?, isContact: Bool, isSettings: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) -> CGFloat { - let avatarSize: CGFloat = isModalOverlay ? 200.0 : 100.0 - let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 22.0), size: CGSize(width: avatarSize, height: avatarSize)) - transition.updateFrameAdditiveToCenter(node: self.avatarNode, frame: CGRect(origin: avatarFrame.center, size: CGSize())) - - var contentHeight: CGFloat = statusBarHeight + 10.0 + avatarSize + 20.0 - - if canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData) { - if self.avatarButtonNode.supernode == nil { - self.addSubnode(self.avatarButtonNode) - } - self.avatarTextNode.attributedText = NSAttributedString(string: presentationData.strings.Settings_SetNewProfilePhotoOrVideo, font: Font.regular(17.0), textColor: presentationData.theme.list.itemAccentColor) - self.avatarButtonNode.accessibilityLabel = self.avatarTextNode.attributedText?.string - - let avatarTextSize = self.avatarTextNode.updateLayout(CGSize(width: width, height: 32.0)) - transition.updateFrame(node: self.avatarTextNode, frame: CGRect(origin: CGPoint(), size: avatarTextSize)) - transition.updateFrame(node: self.avatarButtonNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((width - avatarTextSize.width) / 2.0), y: contentHeight - 1.0), size: avatarTextSize)) - contentHeight += 32.0 - } - - var isEditableBot = false - if let user = peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) { - isEditableBot = true - } - var fieldKeys: [PeerInfoHeaderTextFieldNodeKey] = [] - if let user = peer as? TelegramUser { - if !user.isDeleted { - fieldKeys.append(.firstName) - if isEditableBot { - fieldKeys.append(.description) - } else if user.botInfo == nil { - fieldKeys.append(.lastName) - } - } - } else if let _ = peer as? TelegramGroup { - fieldKeys.append(.title) - if canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData) { - fieldKeys.append(.description) - } - } else if let _ = peer as? TelegramChannel { - fieldKeys.append(.title) - if canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData) { - fieldKeys.append(.description) - } - } - var hasPrevious = false - for key in fieldKeys { - let itemNode: PeerInfoHeaderTextFieldNode - var updateText: String? - if let current = self.itemNodes[key] { - itemNode = current - } else { - var isMultiline = false - switch key { - case .firstName: - if let peer = peer as? TelegramUser { - if let editableBotInfo = (cachedData as? CachedUserData)?.editableBotInfo { - updateText = editableBotInfo.name - } else { - updateText = peer.firstName ?? "" - } - } - case .lastName: - updateText = (peer as? TelegramUser)?.lastName ?? "" - case .title: - updateText = peer?.debugDisplayTitle ?? "" - case .description: - isMultiline = true - if let cachedData = cachedData as? CachedChannelData { - updateText = cachedData.about ?? "" - } else if let cachedData = cachedData as? CachedGroupData { - updateText = cachedData.about ?? "" - } else if let cachedData = cachedData as? CachedUserData { - if let editableBotInfo = cachedData.editableBotInfo { - updateText = editableBotInfo.about - } else { - updateText = cachedData.about ?? "" - } - } else { - updateText = "" - } - } - if isMultiline { - itemNode = PeerInfoHeaderMultiLineTextFieldNode(requestUpdateHeight: { [weak self] in - self?.requestUpdateLayout() - }) - } else { - itemNode = PeerInfoHeaderSingleLineTextFieldNode() - } - self.itemNodes[key] = itemNode - self.addSubnode(itemNode) - } - let placeholder: String - var isEnabled = true - switch key { - case .firstName: - placeholder = isEditableBot ? presentationData.strings.UserInfo_BotNamePlaceholder : presentationData.strings.UserInfo_FirstNamePlaceholder - isEnabled = isContact || isSettings || isEditableBot - case .lastName: - placeholder = presentationData.strings.UserInfo_LastNamePlaceholder - isEnabled = isContact || isSettings - case .title: - if let channel = peer as? TelegramChannel, case .broadcast = channel.info { - placeholder = presentationData.strings.GroupInfo_ChannelListNamePlaceholder - } else { - placeholder = presentationData.strings.GroupInfo_GroupNamePlaceholder - } - isEnabled = canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData) - case .description: - placeholder = presentationData.strings.Channel_Edit_AboutItem - isEnabled = canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData) || isEditableBot - } - let itemHeight = itemNode.update(width: width, safeInset: safeInset, isSettings: isSettings, hasPrevious: hasPrevious, hasNext: key != fieldKeys.last, placeholder: placeholder, isEnabled: isEnabled, presentationData: presentationData, updateText: updateText) - transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: width, height: itemHeight))) - contentHeight += itemHeight - hasPrevious = true - } - var removeKeys: [PeerInfoHeaderTextFieldNodeKey] = [] - for (key, _) in self.itemNodes { - if !fieldKeys.contains(key) { - removeKeys.append(key) - } - } - for key in removeKeys { - if let itemNode = self.itemNodes.removeValue(forKey: key) { - itemNode.removeFromSupernode() - } - } - - return contentHeight - } -} - -private let TitleNodeStateRegular = 0 -private let TitleNodeStateExpanded = 1 - -final class PeerInfoHeaderNode: ASDisplayNode { - private var context: AccountContext - private var presentationData: PresentationData? - private var state: PeerInfoState? - private var peer: Peer? - private var threadData: MessageHistoryThreadData? - private var avatarSize: CGFloat? - - private let isOpenedFromChat: Bool - private let isSettings: Bool - private let videoCallsEnabled: Bool - private let forumTopicThreadId: Int64? - private let chatLocation: ChatLocation - - private(set) var isAvatarExpanded: Bool - var skipCollapseCompletion = false - var ignoreCollapse = false - - let avatarClippingNode: SparseNode - let avatarListNode: PeerInfoAvatarListNode - - let buttonsContainerNode: SparseNode - let regularContentNode: PeerInfoHeaderRegularContentNode - let editingContentNode: PeerInfoHeaderEditingContentNode - let avatarOverlayNode: PeerInfoEditingAvatarOverlayNode - let titleNodeContainer: ASDisplayNode - let titleNodeRawContainer: ASDisplayNode - let titleNode: MultiScaleTextNode - let titleCredibilityIconView: ComponentHostView - var credibilityIconSize: CGSize? - let titleExpandedCredibilityIconView: ComponentHostView - var titleExpandedCredibilityIconSize: CGSize? - let subtitleNodeContainer: ASDisplayNode - let subtitleNodeRawContainer: ASDisplayNode - let subtitleNode: MultiScaleTextNode - var subtitleBackgroundNode: ASDisplayNode? - var subtitleBackgroundButton: HighlightTrackingButtonNode? - var subtitleArrowNode: ASImageNode? - let panelSubtitleNode: MultiScaleTextNode - let nextPanelSubtitleNode: MultiScaleTextNode - let usernameNodeContainer: ASDisplayNode - let usernameNodeRawContainer: ASDisplayNode - let usernameNode: MultiScaleTextNode - var actionButtonNodes: [PeerInfoHeaderButtonKey: PeerInfoHeaderActionButtonNode] = [:] - var buttonNodes: [PeerInfoHeaderButtonKey: PeerInfoHeaderButtonNode] = [:] - let backgroundNode: NavigationBackgroundNode - let expandedBackgroundNode: NavigationBackgroundNode - let separatorNode: ASDisplayNode - let navigationBackgroundNode: ASDisplayNode - let navigationBackgroundBackgroundNode: ASDisplayNode - var navigationTitle: String? - let navigationTitleNode: ImmediateTextNode - let navigationSeparatorNode: ASDisplayNode - let navigationButtonContainer: PeerInfoHeaderNavigationButtonContainerNode - - var performButtonAction: ((PeerInfoHeaderButtonKey, ContextGesture?) -> Void)? - var requestAvatarExpansion: ((Bool, [AvatarGalleryEntry], AvatarGalleryEntry?, (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?) -> Void)? - var requestOpenAvatarForEditing: ((Bool) -> Void)? - var cancelUpload: (() -> Void)? - var requestUpdateLayout: ((Bool) -> Void)? - var animateOverlaysFadeIn: (() -> Void)? - - var displayAvatarContextMenu: ((ASDisplayNode, ContextGesture?) -> Void)? - var displayCopyContextMenu: ((ASDisplayNode, Bool, Bool) -> Void)? - var displayEmojiPackTooltip: (() -> Void)? - - var displayPremiumIntro: ((UIView, PeerEmojiStatus?, Signal<(TelegramMediaFile, LoadedStickerPack)?, NoError>, Bool) -> Void)? - - var navigateToForum: (() -> Void)? - - var navigationTransition: PeerInfoHeaderNavigationTransition? - - var backgroundAlpha: CGFloat = 1.0 - var updateHeaderAlpha: ((CGFloat, ContainedViewLayoutTransition) -> Void)? - - let animationCache: AnimationCache - let animationRenderer: MultiAnimationRenderer - - var emojiStatusPackDisposable = MetaDisposable() - var emojiStatusFileAndPackTitle = Promise<(TelegramMediaFile, LoadedStickerPack)?>() - - private var validLayout: (width: CGFloat, deviceMetrics: DeviceMetrics)? - - init(context: AccountContext, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, isMediaOnly: Bool, isSettings: Bool, forumTopicThreadId: Int64?, chatLocation: ChatLocation) { - self.context = context - self.isAvatarExpanded = avatarInitiallyExpanded - self.isOpenedFromChat = isOpenedFromChat - self.isSettings = isSettings - self.videoCallsEnabled = true - self.forumTopicThreadId = forumTopicThreadId - self.chatLocation = chatLocation - - self.avatarClippingNode = SparseNode() - self.avatarClippingNode.clipsToBounds = true - self.avatarListNode = PeerInfoAvatarListNode(context: context, readyWhenGalleryLoads: avatarInitiallyExpanded, isSettings: isSettings) - - self.titleNodeContainer = ASDisplayNode() - self.titleNodeRawContainer = ASDisplayNode() - self.titleNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded]) - self.titleNode.displaysAsynchronously = false - - self.titleCredibilityIconView = ComponentHostView() - self.titleNode.stateNode(forKey: TitleNodeStateRegular)?.view.addSubview(self.titleCredibilityIconView) - - self.titleExpandedCredibilityIconView = ComponentHostView() - self.titleNode.stateNode(forKey: TitleNodeStateExpanded)?.view.addSubview(self.titleExpandedCredibilityIconView) - - self.subtitleNodeContainer = ASDisplayNode() - self.subtitleNodeRawContainer = ASDisplayNode() - self.subtitleNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded]) - self.subtitleNode.displaysAsynchronously = false - - self.panelSubtitleNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded]) - self.panelSubtitleNode.displaysAsynchronously = false - - self.nextPanelSubtitleNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded]) - self.nextPanelSubtitleNode.displaysAsynchronously = false - - self.usernameNodeContainer = ASDisplayNode() - self.usernameNodeRawContainer = ASDisplayNode() - self.usernameNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded]) - self.usernameNode.displaysAsynchronously = false - - self.buttonsContainerNode = SparseNode() - self.buttonsContainerNode.clipsToBounds = true - - self.regularContentNode = PeerInfoHeaderRegularContentNode() - var requestUpdateLayoutImpl: (() -> Void)? - self.editingContentNode = PeerInfoHeaderEditingContentNode(context: context, requestUpdateLayout: { - requestUpdateLayoutImpl?() - }) - self.editingContentNode.alpha = 0.0 - - self.avatarOverlayNode = PeerInfoEditingAvatarOverlayNode(context: context) - self.avatarOverlayNode.isUserInteractionEnabled = false - - self.navigationBackgroundNode = ASDisplayNode() - self.navigationBackgroundNode.isHidden = true - self.navigationBackgroundNode.isUserInteractionEnabled = false - - self.navigationBackgroundBackgroundNode = ASDisplayNode() - self.navigationBackgroundBackgroundNode.isUserInteractionEnabled = false - - self.navigationTitleNode = ImmediateTextNode() - - self.navigationSeparatorNode = ASDisplayNode() - - self.navigationButtonContainer = PeerInfoHeaderNavigationButtonContainerNode() - - self.backgroundNode = NavigationBackgroundNode(color: .clear) - self.backgroundNode.isHidden = true - self.backgroundNode.isUserInteractionEnabled = false - self.expandedBackgroundNode = NavigationBackgroundNode(color: .clear) - self.expandedBackgroundNode.isHidden = false - self.expandedBackgroundNode.isUserInteractionEnabled = false - - self.separatorNode = ASDisplayNode() - self.separatorNode.isLayerBacked = true - - self.animationCache = context.animationCache - self.animationRenderer = context.animationRenderer - - super.init() - - requestUpdateLayoutImpl = { [weak self] in - self?.requestUpdateLayout?(false) - } - - if !isMediaOnly { - self.addSubnode(self.buttonsContainerNode) - } - self.addSubnode(self.backgroundNode) - self.addSubnode(self.expandedBackgroundNode) - self.titleNodeContainer.addSubnode(self.titleNode) - self.subtitleNodeContainer.addSubnode(self.subtitleNode) - self.subtitleNodeContainer.addSubnode(self.panelSubtitleNode) -// self.subtitleNodeContainer.addSubnode(self.nextPanelSubtitleNode) - self.usernameNodeContainer.addSubnode(self.usernameNode) - - self.regularContentNode.addSubnode(self.avatarClippingNode) - self.avatarClippingNode.addSubnode(self.avatarListNode) - self.regularContentNode.addSubnode(self.avatarListNode.listContainerNode.controlsClippingOffsetNode) - self.regularContentNode.addSubnode(self.titleNodeContainer) - self.regularContentNode.addSubnode(self.subtitleNodeContainer) - self.regularContentNode.addSubnode(self.subtitleNodeRawContainer) - self.regularContentNode.addSubnode(self.usernameNodeContainer) - self.regularContentNode.addSubnode(self.usernameNodeRawContainer) - - self.addSubnode(self.regularContentNode) - self.addSubnode(self.editingContentNode) - self.addSubnode(self.avatarOverlayNode) - self.addSubnode(self.navigationBackgroundNode) - self.navigationBackgroundNode.addSubnode(self.navigationBackgroundBackgroundNode) - self.navigationBackgroundNode.addSubnode(self.navigationTitleNode) - self.navigationBackgroundNode.addSubnode(self.navigationSeparatorNode) - self.addSubnode(self.navigationButtonContainer) - self.addSubnode(self.separatorNode) - - self.avatarListNode.avatarContainerNode.tapped = { [weak self] in - self?.initiateAvatarExpansion(gallery: false, first: false) - } - self.avatarListNode.avatarContainerNode.contextAction = { [weak self] node, gesture in - self?.displayAvatarContextMenu?(node, gesture) - } - self.avatarListNode.avatarContainerNode.emojiTapped = { [weak self] in - self?.displayEmojiPackTooltip?() - } - - self.editingContentNode.avatarNode.tapped = { [weak self] confirm in - self?.initiateAvatarExpansion(gallery: true, first: true) - } - self.editingContentNode.requestEditing = { [weak self] in - self?.requestOpenAvatarForEditing?(true) - } - - self.avatarListNode.itemsUpdated = { [weak self] items in - guard let strongSelf = self, let state = strongSelf.state, let peer = strongSelf.peer, let presentationData = strongSelf.presentationData, let avatarSize = strongSelf.avatarSize else { - return - } - strongSelf.editingContentNode.avatarNode.update(peer: peer, threadData: strongSelf.threadData, chatLocation: chatLocation, item: strongSelf.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing) - } - - self.avatarListNode.animateOverlaysFadeIn = { [weak self] in - guard let strongSelf = self else { - return - } - strongSelf.navigationButtonContainer.layer.animateAlpha(from: 0.0, to: strongSelf.navigationButtonContainer.alpha, duration: 0.25) - strongSelf.avatarListNode.listContainerNode.topShadowNode.layer.animateAlpha(from: 0.0, to: strongSelf.avatarListNode.listContainerNode.topShadowNode.alpha, duration: 0.25) - - strongSelf.avatarListNode.listContainerNode.bottomShadowNode.alpha = 1.0 - strongSelf.avatarListNode.listContainerNode.bottomShadowNode.layer.animateAlpha(from: 0.0, to: strongSelf.avatarListNode.listContainerNode.bottomShadowNode.alpha, duration: 0.25) - strongSelf.avatarListNode.listContainerNode.controlsContainerNode.layer.animateAlpha(from: 0.0, to: strongSelf.avatarListNode.listContainerNode.controlsContainerNode.alpha, duration: 0.25) - - strongSelf.titleNode.layer.animateAlpha(from: 0.0, to: strongSelf.titleNode.alpha, duration: 0.25) - strongSelf.subtitleNode.layer.animateAlpha(from: 0.0, to: strongSelf.subtitleNode.alpha, duration: 0.25) - - strongSelf.animateOverlaysFadeIn?() - } - } - - deinit { - self.emojiStatusPackDisposable.dispose() - } - - override func didLoad() { - super.didLoad() - - let usernameGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.handleUsernameLongPress(_:))) - self.usernameNodeRawContainer.view.addGestureRecognizer(usernameGestureRecognizer) - - let phoneGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.handlePhoneLongPress(_:))) - self.subtitleNodeRawContainer.view.addGestureRecognizer(phoneGestureRecognizer) - } - - @objc private func handleUsernameLongPress(_ gestureRecognizer: UILongPressGestureRecognizer) { - if gestureRecognizer.state == .began { - self.displayCopyContextMenu?(self.usernameNodeRawContainer, !self.isAvatarExpanded, true) - } - } - - @objc private func handlePhoneLongPress(_ gestureRecognizer: UILongPressGestureRecognizer) { - if gestureRecognizer.state == .began { - self.displayCopyContextMenu?(self.subtitleNodeRawContainer, true, !self.isAvatarExpanded) - } - } - - @objc private func subtitleBackgroundPressed() { - self.navigateToForum?() - } - - func invokeDisplayPremiumIntro() { - self.displayPremiumIntro?(self.isAvatarExpanded ? self.titleExpandedCredibilityIconView : self.titleCredibilityIconView, nil, .never(), self.isAvatarExpanded) - } - - func initiateAvatarExpansion(gallery: Bool, first: Bool) { - if let peer = self.peer, peer.profileImageRepresentations.isEmpty && gallery { - self.requestOpenAvatarForEditing?(false) - return - } - if self.isAvatarExpanded || gallery { - if let currentEntry = self.avatarListNode.listContainerNode.currentEntry, let firstEntry = self.avatarListNode.listContainerNode.galleryEntries.first { - let entry = first ? firstEntry : currentEntry - self.requestAvatarExpansion?(true, self.avatarListNode.listContainerNode.galleryEntries, entry, self.avatarTransitionArguments(entry: currentEntry)) - } - } else if let entry = self.avatarListNode.listContainerNode.galleryEntries.first { - self.requestAvatarExpansion?(false, self.avatarListNode.listContainerNode.galleryEntries, nil, self.avatarTransitionArguments(entry: entry)) - } else if let storyParams = self.avatarListNode.listContainerNode.storyParams, storyParams.count != 0 { - self.requestAvatarExpansion?(false, self.avatarListNode.listContainerNode.galleryEntries, nil, nil) - } else { - self.cancelUpload?() - } - } - - func avatarTransitionArguments(entry: AvatarGalleryEntry) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? { - if self.isAvatarExpanded { - if let avatarNode = self.avatarListNode.listContainerNode.currentItemNode?.imageNode { - return (avatarNode, avatarNode.bounds, { [weak avatarNode] in - return (avatarNode?.view.snapshotContentTree(unhide: true), nil) - }) - } else { - return nil - } - } else if entry == self.avatarListNode.listContainerNode.galleryEntries.first { - let avatarNode = self.avatarListNode.avatarContainerNode.avatarNode - return (avatarNode, avatarNode.bounds, { [weak avatarNode] in - return (avatarNode?.view.snapshotContentTree(unhide: true), nil) - }) - } else { - return nil - } - } - - func addToAvatarTransitionSurface(view: UIView) { - if self.isAvatarExpanded { - self.avatarListNode.listContainerNode.view.addSubview(view) - } else { - self.view.addSubview(view) - } - } - - func updateAvatarIsHidden(entry: AvatarGalleryEntry?) { - if let entry = entry { - self.avatarListNode.avatarContainerNode.containerNode.isHidden = entry == self.avatarListNode.listContainerNode.galleryEntries.first - self.editingContentNode.avatarNode.isHidden = entry == self.avatarListNode.listContainerNode.galleryEntries.first - } else { - self.avatarListNode.avatarContainerNode.containerNode.isHidden = false - self.editingContentNode.avatarNode.isHidden = false - } - self.avatarListNode.listContainerNode.updateEntryIsHidden(entry: entry) - } - - private enum CredibilityIcon: Equatable { - case none - case premium - case verified - case fake - case scam - case emojiStatus(PeerEmojiStatus) - } - - private var currentCredibilityIcon: 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, metrics: LayoutMetrics, deviceMetrics: DeviceMetrics, transition: ContainedViewLayoutTransition, additive: Bool) -> CGFloat { - self.state = state - self.peer = peer - self.threadData = threadData - self.avatarListNode.listContainerNode.peer = peer.flatMap(EnginePeer.init) - - let isFirstTime = self.validLayout == nil - self.validLayout = (width, deviceMetrics) - - let previousPanelStatusData = self.currentPanelStatusData - self.currentPanelStatusData = panelStatusData.0 - - let avatarSize: CGFloat = isModalOverlay ? 200.0 : 100.0 - self.avatarSize = avatarSize - - var contentOffset = contentOffset - - if isMediaOnly { - if isModalOverlay { - contentOffset = 312.0 - } else { - contentOffset = 212.0 - } - } - - let isLandscape = containerInset > 16.0 - - let themeUpdated = self.presentationData?.theme !== presentationData.theme - self.presentationData = presentationData - - let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 }) - let credibilityIcon: CredibilityIcon - if let peer = peer { - if peer.isFake { - credibilityIcon = .fake - } else if peer.isScam { - credibilityIcon = .scam - } else if let user = peer as? TelegramUser, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled { - credibilityIcon = .emojiStatus(emojiStatus) - } else if peer.isVerified { - credibilityIcon = .verified - } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled && (peer.id != self.context.account.peerId || self.isSettings) { - credibilityIcon = .premium - } else { - credibilityIcon = .none - } - } else { - credibilityIcon = .none - } - - var isForum = false - if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) { - isForum = true - } - - if themeUpdated || self.currentCredibilityIcon != credibilityIcon { - self.currentCredibilityIcon = credibilityIcon - - var currentEmojiStatus: PeerEmojiStatus? - let emojiRegularStatusContent: EmojiStatusComponent.Content - let emojiExpandedStatusContent: EmojiStatusComponent.Content - switch credibilityIcon { - case .none: - emojiRegularStatusContent = .none - emojiExpandedStatusContent = .none - case .premium: - emojiRegularStatusContent = .premium(color: presentationData.theme.list.itemAccentColor) - emojiExpandedStatusContent = .premium(color: UIColor(rgb: 0xffffff, alpha: 0.75)) - case .verified: - emojiRegularStatusContent = .verified(fillColor: presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .large) - emojiExpandedStatusContent = .verified(fillColor: UIColor(rgb: 0xffffff, alpha: 0.75), foregroundColor: .clear, sizeType: .large) - case .fake: - emojiRegularStatusContent = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_FakeAccount.uppercased()) - emojiExpandedStatusContent = emojiRegularStatusContent - case .scam: - emojiRegularStatusContent = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_ScamAccount.uppercased()) - emojiExpandedStatusContent = emojiRegularStatusContent - case let .emojiStatus(emojiStatus): - currentEmojiStatus = emojiStatus - emojiRegularStatusContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 80.0, height: 80.0), placeholderColor: presentationData.theme.list.mediaPlaceholderColor, themeColor: presentationData.theme.list.itemAccentColor, loopMode: .forever) - emojiExpandedStatusContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 80.0, height: 80.0), placeholderColor: UIColor(rgb: 0xffffff, alpha: 0.15), themeColor: presentationData.theme.list.itemAccentColor, loopMode: .forever) - } - - let animateStatusIcon = !self.titleCredibilityIconView.bounds.isEmpty - - let iconSize = self.titleCredibilityIconView.update( - transition: animateStatusIcon ? Transition(animation: .curve(duration: 0.3, curve: .easeInOut)) : .immediate, - component: AnyComponent(EmojiStatusComponent( - context: self.context, - animationCache: self.animationCache, - animationRenderer: self.animationRenderer, - content: emojiRegularStatusContent, - isVisibleForAnimations: true, - useSharedAnimation: true, - action: { [weak self] in - guard let strongSelf = self else { - return - } - strongSelf.displayPremiumIntro?(strongSelf.titleCredibilityIconView, currentEmojiStatus, strongSelf.emojiStatusFileAndPackTitle.get(), false) - }, - emojiFileUpdated: { [weak self] emojiFile in - guard let strongSelf = self else { - return - } - - if let emojiFile = emojiFile { - strongSelf.emojiStatusFileAndPackTitle.set(.never()) - - for attribute in emojiFile.attributes { - if case let .CustomEmoji(_, _, _, packReference) = attribute, let packReference = packReference { - strongSelf.emojiStatusPackDisposable.set((strongSelf.context.engine.stickers.loadedStickerPack(reference: packReference, forceActualized: false) - |> filter { result in - if case .result = result { - return true - } else { - return false - } - } - |> mapToSignal { result -> Signal<(TelegramMediaFile, LoadedStickerPack)?, NoError> in - if case let .result(_, items, _) = result { - return .single(items.first.flatMap { ($0.file, result) }) - } else { - return .complete() - } - }).startStrict(next: { fileAndPackTitle in - guard let strongSelf = self else { - return - } - strongSelf.emojiStatusFileAndPackTitle.set(.single(fileAndPackTitle)) - })) - break - } - } - } else { - strongSelf.emojiStatusFileAndPackTitle.set(.never()) - } - } - )), - environment: {}, - containerSize: CGSize(width: 34.0, height: 34.0) - ) - let expandedIconSize = self.titleExpandedCredibilityIconView.update( - transition: animateStatusIcon ? Transition(animation: .curve(duration: 0.3, curve: .easeInOut)) : .immediate, - component: AnyComponent(EmojiStatusComponent( - context: self.context, - animationCache: self.animationCache, - animationRenderer: self.animationRenderer, - content: emojiExpandedStatusContent, - isVisibleForAnimations: true, - useSharedAnimation: true, - action: { [weak self] in - guard let strongSelf = self else { - return - } - strongSelf.displayPremiumIntro?(strongSelf.titleExpandedCredibilityIconView, currentEmojiStatus, strongSelf.emojiStatusFileAndPackTitle.get(), true) - } - )), - environment: {}, - containerSize: CGSize(width: 34.0, height: 34.0) - ) - - self.credibilityIconSize = iconSize - self.titleExpandedCredibilityIconSize = expandedIconSize - } - - self.regularContentNode.alpha = state.isEditing ? 0.0 : 1.0 - self.buttonsContainerNode.alpha = self.regularContentNode.alpha - self.editingContentNode.alpha = state.isEditing ? 1.0 : 0.0 - - let editingContentHeight = self.editingContentNode.update(width: width, safeInset: containerInset, statusBarHeight: statusBarHeight, navigationHeight: navigationHeight, isModalOverlay: isModalOverlay, peer: state.isEditing ? peer : nil, threadData: threadData, chatLocation: self.chatLocation, cachedData: cachedData, isContact: isContact, isSettings: isSettings, presentationData: presentationData, transition: transition) - transition.updateFrame(node: self.editingContentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -contentOffset), size: CGSize(width: width, height: editingContentHeight))) - - let avatarOverlayFarme = self.editingContentNode.convert(self.editingContentNode.avatarNode.frame, to: self) - transition.updateFrame(node: self.avatarOverlayNode, frame: avatarOverlayFarme) - - var transitionSourceHeight: CGFloat = 0.0 - var transitionFraction: CGFloat = 0.0 - var transitionSourceAvatarFrame: CGRect? - var transitionSourceTitleFrame = CGRect() - var transitionSourceSubtitleFrame = CGRect() - - let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 22.0), size: CGSize(width: avatarSize, height: avatarSize)) - - self.backgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) - - let headerBackgroundColor: UIColor = presentationData.theme.list.blocksBackgroundColor - var effectiveSeparatorAlpha: CGFloat - if let navigationTransition = self.navigationTransition { - transitionSourceHeight = navigationTransition.sourceNavigationBar.backgroundNode.bounds.height - transitionFraction = navigationTransition.fraction - - if let avatarNavigationNode = navigationTransition.sourceNavigationBar.rightButtonNode.singleCustomNode as? ChatAvatarNavigationNode { - if let statusView = avatarNavigationNode.statusView.view { - transitionSourceAvatarFrame = statusView.convert(statusView.bounds, to: navigationTransition.sourceNavigationBar.view) - } else { - transitionSourceAvatarFrame = avatarNavigationNode.avatarNode.view.convert(avatarNavigationNode.avatarNode.view.bounds, to: navigationTransition.sourceNavigationBar.view) - } - transition.updateAlpha(node: self.avatarListNode.avatarContainerNode.avatarNode, alpha: 1.0 - transitionFraction) - } else { - if deviceMetrics.hasDynamicIsland && !isLandscape { - transitionSourceAvatarFrame = CGRect(origin: CGPoint(x: avatarFrame.minX, y: -20.0), size: avatarFrame.size).insetBy(dx: avatarSize * 0.4, dy: avatarSize * 0.4) - } else { - transitionSourceAvatarFrame = avatarFrame.offsetBy(dx: 0.0, dy: -avatarFrame.maxY).insetBy(dx: avatarSize * 0.4, dy: avatarSize * 0.4) - } - } - transitionSourceTitleFrame = navigationTransition.sourceTitleFrame - transitionSourceSubtitleFrame = navigationTransition.sourceSubtitleFrame - - self.expandedBackgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor.mixedWith(headerBackgroundColor, alpha: 1.0 - transitionFraction), forceKeepBlur: true, transition: transition) - effectiveSeparatorAlpha = transitionFraction - - if self.isAvatarExpanded, case .animated = transition, transitionFraction == 1.0 { - self.avatarListNode.animateAvatarCollapse(transition: transition) - } - self.avatarClippingNode.clipsToBounds = false - } else { - let contentOffset = max(0.0, contentOffset - 140.0) - let backgroundTransitionFraction: CGFloat = max(0.0, min(1.0, contentOffset / 30.0)) - - self.expandedBackgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.opaqueBackgroundColor.mixedWith(headerBackgroundColor, alpha: 1.0 - backgroundTransitionFraction), forceKeepBlur: true, transition: transition) - effectiveSeparatorAlpha = backgroundTransitionFraction - - self.avatarClippingNode.clipsToBounds = true - } - - self.avatarListNode.avatarContainerNode.updateTransitionFraction(transitionFraction, transition: transition) - self.avatarListNode.listContainerNode.currentItemNode?.updateTransitionFraction(transitionFraction, transition: transition) - self.avatarOverlayNode.updateTransitionFraction(transitionFraction, transition: transition) - - if self.navigationTitle != presentationData.strings.EditProfile_Title || themeUpdated { - self.navigationTitleNode.attributedText = NSAttributedString(string: presentationData.strings.EditProfile_Title, font: Font.semibold(17.0), textColor: presentationData.theme.rootController.navigationBar.primaryTextColor) - } - - let navigationTitleSize = self.navigationTitleNode.updateLayout(CGSize(width: width, height: navigationHeight)) - self.navigationTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - navigationTitleSize.width) / 2.0), y: navigationHeight - 44.0 + floorToScreenPixels((44.0 - navigationTitleSize.height) / 2.0)), size: navigationTitleSize) - - self.navigationBackgroundNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: navigationHeight)) - self.navigationBackgroundBackgroundNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: navigationHeight)) - self.navigationSeparatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: navigationHeight), size: CGSize(width: width, height: UIScreenPixel)) - self.navigationBackgroundBackgroundNode.backgroundColor = presentationData.theme.rootController.navigationBar.opaqueBackgroundColor - self.navigationSeparatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor - - let navigationSeparatorAlpha: CGFloat = state.isEditing && self.isSettings ? min(1.0, contentOffset / (navigationHeight * 0.5)) : 0.0 - transition.updateAlpha(node: self.navigationBackgroundBackgroundNode, alpha: 1.0 - navigationSeparatorAlpha) - transition.updateAlpha(node: self.navigationSeparatorNode, alpha: navigationSeparatorAlpha) - - self.separatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor - - let expandedAvatarControlsHeight: CGFloat = 61.0 - let expandedAvatarListHeight = min(width, containerHeight - expandedAvatarControlsHeight) - let expandedAvatarListSize = CGSize(width: width, height: expandedAvatarListHeight) - - let actionButtonKeys: [PeerInfoHeaderButtonKey] = self.isSettings ? [] : peerInfoHeaderActionButtons(peer: peer, isSecretChat: isSecretChat, isContact: isContact) - let buttonKeys: [PeerInfoHeaderButtonKey] = self.isSettings ? [] : 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 - let titleStringText: String - let smallTitleAttributes: MultiScaleTextState.Attributes - let titleAttributes: MultiScaleTextState.Attributes - let subtitleStringText: String - let smallSubtitleAttributes: MultiScaleTextState.Attributes - let subtitleAttributes: MultiScaleTextState.Attributes - var subtitleIsButton: Bool = false - var panelSubtitleString: (text: String, attributes: MultiScaleTextState.Attributes)? - var nextPanelSubtitleString: (text: String, attributes: MultiScaleTextState.Attributes)? - let usernameString: (text: String, attributes: MultiScaleTextState.Attributes) - if let peer = peer { - isPremium = peer.isPremium - isVerified = peer.isVerified - isFake = peer.isFake || peer.isScam - } - - if let peer = peer { - var title: String - if peer.id == self.context.account.peerId && !self.isSettings { - title = presentationData.strings.Conversation_SavedMessages - } else if peer.id == self.context.account.peerId && !self.isSettings { - title = presentationData.strings.DialogList_Replies - } else if let threadData = threadData { - title = threadData.info.title - } else { - title = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - } - title = title.replacingOccurrences(of: "\u{1160}", with: "").replacingOccurrences(of: "\u{3164}", with: "") - // MARK: Nicegram Hide phone - if title.isEmpty { - if let peer = peer as? TelegramUser, let phone = peer.phone, !NGSettings.hidePhoneSettings { - title = formatPhoneNumber(context: self.context, number: phone) - } else if let addressName = peer.addressName { - title = "@\(addressName)" - } else { - title = " " - } - } - - titleStringText = title - titleAttributes = MultiScaleTextState.Attributes(font: Font.medium(30.0), color: presentationData.theme.list.itemPrimaryTextColor) - smallTitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(30.0), color: .white) - - if self.isSettings, let user = peer as? TelegramUser { - // MARK: Nicegram Hide phone - var formattedPhone = formatPhoneNumber(context: self.context, number: user.phone ?? "") - if !formattedPhone.isEmpty && NGSettings.hidePhoneSettings { - formattedPhone = "" - } - // MARK - var subtitle = formattedPhone - - if let mainUsername = user.addressName, !mainUsername.isEmpty { - if !subtitle.isEmpty { - subtitle = "\(subtitle) • @\(mainUsername)" - } else { - subtitle = "@\(mainUsername)" - } - } - subtitleStringText = subtitle - subtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(17.0), color: presentationData.theme.list.itemSecondaryTextColor) - smallSubtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(15.0), color: UIColor(white: 1.0, alpha: 0.7)) - - usernameString = ("", MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor)) - } else if let _ = threadData { - let subtitleColor: UIColor - subtitleColor = presentationData.theme.list.itemAccentColor - - let statusText: String - statusText = peer.debugDisplayTitle - - subtitleStringText = statusText - subtitleAttributes = MultiScaleTextState.Attributes(font: Font.semibold(15.0), color: subtitleColor) - smallSubtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(15.0), color: UIColor(white: 1.0, alpha: 0.7)) - - usernameString = ("", MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor)) - - subtitleIsButton = true - - let (maybePanelStatusData, maybeNextPanelStatusData, _) = panelStatusData - if let panelStatusData = maybePanelStatusData { - let subtitleColor: UIColor - if panelStatusData.isActivity { - subtitleColor = presentationData.theme.list.itemAccentColor - } else { - subtitleColor = presentationData.theme.list.itemSecondaryTextColor - } - panelSubtitleString = (panelStatusData.text, MultiScaleTextState.Attributes(font: Font.regular(17.0), color: subtitleColor)) - } - if let nextPanelStatusData = maybeNextPanelStatusData { - nextPanelSubtitleString = (nextPanelStatusData.text, MultiScaleTextState.Attributes(font: Font.regular(17.0), color: presentationData.theme.list.itemSecondaryTextColor)) - } - } else if let statusData = statusData { - let subtitleColor: UIColor - if statusData.isActivity { - subtitleColor = presentationData.theme.list.itemAccentColor - } else { - subtitleColor = presentationData.theme.list.itemSecondaryTextColor - } - - subtitleStringText = statusData.text - subtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(17.0), color: subtitleColor) - smallSubtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(15.0), color: UIColor(white: 1.0, alpha: 0.7)) - - usernameString = ("", MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor)) - - let (maybePanelStatusData, maybeNextPanelStatusData, _) = panelStatusData - if let panelStatusData = maybePanelStatusData { - let subtitleColor: UIColor - if panelStatusData.isActivity { - subtitleColor = presentationData.theme.list.itemAccentColor - } else { - subtitleColor = presentationData.theme.list.itemSecondaryTextColor - } - panelSubtitleString = (panelStatusData.text, MultiScaleTextState.Attributes(font: Font.regular(17.0), color: subtitleColor)) - } - if let nextPanelStatusData = maybeNextPanelStatusData { - nextPanelSubtitleString = (nextPanelStatusData.text, MultiScaleTextState.Attributes(font: Font.regular(17.0), color: presentationData.theme.list.itemSecondaryTextColor)) - } - } else { - subtitleStringText = " " - subtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor) - smallSubtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor) - - usernameString = ("", MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor)) - } - } else { - titleStringText = " " - titleAttributes = MultiScaleTextState.Attributes(font: Font.regular(24.0), color: presentationData.theme.list.itemPrimaryTextColor) - smallTitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(24.0), color: .white) - - subtitleStringText = " " - subtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor) - smallSubtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor) - - usernameString = ("", MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor)) - } - - let textSideInset: CGFloat = 36.0 - let expandedAvatarHeight: CGFloat = expandedAvatarListSize.height - - let titleConstrainedSize = CGSize(width: width - textSideInset * 2.0 - (isPremium || isVerified || isFake ? 20.0 : 0.0), height: .greatestFiniteMagnitude) - - let titleNodeLayout = self.titleNode.updateLayout(text: titleStringText, states: [ - TitleNodeStateRegular: MultiScaleTextState(attributes: titleAttributes, constrainedSize: titleConstrainedSize), - TitleNodeStateExpanded: MultiScaleTextState(attributes: smallTitleAttributes, constrainedSize: titleConstrainedSize) - ], mainState: TitleNodeStateRegular) - - let subtitleNodeLayout = self.subtitleNode.updateLayout(text: subtitleStringText, states: [ - TitleNodeStateRegular: MultiScaleTextState(attributes: subtitleAttributes, constrainedSize: titleConstrainedSize), - TitleNodeStateExpanded: MultiScaleTextState(attributes: smallSubtitleAttributes, constrainedSize: titleConstrainedSize) - ], mainState: TitleNodeStateRegular) - self.subtitleNode.accessibilityLabel = subtitleStringText - - if subtitleIsButton { - let subtitleBackgroundNode: ASDisplayNode - if let current = self.subtitleBackgroundNode { - subtitleBackgroundNode = current - } else { - subtitleBackgroundNode = ASDisplayNode() - self.subtitleBackgroundNode = subtitleBackgroundNode - self.subtitleNode.insertSubnode(subtitleBackgroundNode, at: 0) - } - - let subtitleBackgroundButton: HighlightTrackingButtonNode - if let current = self.subtitleBackgroundButton { - subtitleBackgroundButton = current - } else { - subtitleBackgroundButton = HighlightTrackingButtonNode() - self.subtitleBackgroundButton = subtitleBackgroundButton - self.subtitleNode.addSubnode(subtitleBackgroundButton) - - subtitleBackgroundButton.addTarget(self, action: #selector(self.subtitleBackgroundPressed), forControlEvents: .touchUpInside) - subtitleBackgroundButton.highligthedChanged = { [weak self] highlighted in - guard let self else { - return - } - if highlighted { - self.subtitleNode.layer.removeAnimation(forKey: "opacity") - self.subtitleNode.alpha = 0.4 - } else { - self.subtitleNode.alpha = 1.0 - self.subtitleNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) - } - } - } - - let subtitleArrowNode: ASImageNode - if let current = self.subtitleArrowNode { - subtitleArrowNode = current - if themeUpdated { - subtitleArrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Item List/DisclosureArrow"), color: presentationData.theme.list.itemAccentColor.withMultipliedAlpha(0.5)) - } - } else { - subtitleArrowNode = ASImageNode() - self.subtitleArrowNode = subtitleArrowNode - self.subtitleNode.insertSubnode(subtitleArrowNode, at: 1) - - subtitleArrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Item List/DisclosureArrow"), color: presentationData.theme.list.itemAccentColor.withMultipliedAlpha(0.5)) - } - subtitleBackgroundNode.backgroundColor = presentationData.theme.list.itemAccentColor.withMultipliedAlpha(0.1) - let subtitleSize = subtitleNodeLayout[TitleNodeStateRegular]!.size - var subtitleBackgroundFrame = CGRect(origin: CGPoint(), size: subtitleSize).offsetBy(dx: -subtitleSize.width * 0.5, dy: -subtitleSize.height * 0.5).insetBy(dx: -6.0, dy: -4.0) - subtitleBackgroundFrame.size.width += 12.0 - transition.updateFrame(node: subtitleBackgroundNode, frame: subtitleBackgroundFrame) - transition.updateCornerRadius(node: subtitleBackgroundNode, cornerRadius: subtitleBackgroundFrame.height * 0.5) - - transition.updateFrame(node: subtitleBackgroundButton, frame: subtitleBackgroundFrame) - - if let arrowImage = subtitleArrowNode.image { - let scaleFactor: CGFloat = 0.8 - let arrowSize = CGSize(width: floorToScreenPixels(arrowImage.size.width * scaleFactor), height: floorToScreenPixels(arrowImage.size.height * scaleFactor)) - subtitleArrowNode.frame = CGRect(origin: CGPoint(x: subtitleBackgroundFrame.maxX - arrowSize.width - 1.0, y: subtitleBackgroundFrame.minY + floor((subtitleBackgroundFrame.height - arrowSize.height) / 2.0)), size: arrowSize) - } - } else { - if let subtitleBackgroundNode = self.subtitleBackgroundNode { - self.subtitleBackgroundNode = nil - subtitleBackgroundNode.removeFromSupernode() - } - if let subtitleArrowNode = self.subtitleArrowNode { - self.subtitleArrowNode = nil - subtitleArrowNode.removeFromSupernode() - } - if let subtitleBackgroundButton = self.subtitleBackgroundButton { - self.subtitleBackgroundButton = nil - subtitleBackgroundButton.removeFromSupernode() - } - } - - 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 - - self.panelSubtitleNode.view.superview?.addSubview(snapshotView) - snapshotView.frame = self.panelSubtitleNode.frame - snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 100.0 * direction, y: 0.0), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true, completion: { [weak snapshotView] _ in - snapshotView?.removeFromSuperview() - }) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) - - self.panelSubtitleNode.layer.animatePosition(from: CGPoint(x: 100.0 * direction * -1.0, y: 0.0), to: CGPoint(), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true) - self.panelSubtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - } - } - - let panelSubtitleNodeLayout = self.panelSubtitleNode.updateLayout(text: panelSubtitleString?.text ?? subtitleStringText, states: [ - TitleNodeStateRegular: MultiScaleTextState(attributes: panelSubtitleString?.attributes ?? subtitleAttributes, constrainedSize: titleConstrainedSize), - TitleNodeStateExpanded: MultiScaleTextState(attributes: panelSubtitleString?.attributes ?? subtitleAttributes, constrainedSize: titleConstrainedSize) - ], mainState: TitleNodeStateRegular) - self.panelSubtitleNode.accessibilityLabel = panelSubtitleString?.text ?? subtitleStringText - - let nextPanelSubtitleNodeLayout = self.nextPanelSubtitleNode.updateLayout(text: nextPanelSubtitleString?.text ?? subtitleStringText, states: [ - TitleNodeStateRegular: MultiScaleTextState(attributes: nextPanelSubtitleString?.attributes ?? subtitleAttributes, constrainedSize: titleConstrainedSize), - TitleNodeStateExpanded: MultiScaleTextState(attributes: nextPanelSubtitleString?.attributes ?? subtitleAttributes, constrainedSize: titleConstrainedSize) - ], mainState: TitleNodeStateRegular) - if let _ = nextPanelSubtitleString { - self.nextPanelSubtitleNode.isHidden = false - } - - let usernameNodeLayout = self.usernameNode.updateLayout(text: usernameString.text, states: [ - TitleNodeStateRegular: MultiScaleTextState(attributes: usernameString.attributes, constrainedSize: CGSize(width: titleConstrainedSize.width, height: titleConstrainedSize.height)), - TitleNodeStateExpanded: MultiScaleTextState(attributes: usernameString.attributes, constrainedSize: CGSize(width: width - titleNodeLayout[TitleNodeStateExpanded]!.size.width - 8.0, height: titleConstrainedSize.height)) - ], mainState: TitleNodeStateRegular) - self.usernameNode.accessibilityLabel = usernameString.text - - let avatarCenter: CGPoint - if let transitionSourceAvatarFrame = transitionSourceAvatarFrame { - avatarCenter = CGPoint(x: (1.0 - transitionFraction) * avatarFrame.midX + transitionFraction * transitionSourceAvatarFrame.midX, y: (1.0 - transitionFraction) * avatarFrame.midY + transitionFraction * transitionSourceAvatarFrame.midY) - } else { - avatarCenter = avatarFrame.center - } - - let titleSize = titleNodeLayout[TitleNodeStateRegular]!.size - let titleExpandedSize = titleNodeLayout[TitleNodeStateExpanded]!.size - let subtitleSize = subtitleNodeLayout[TitleNodeStateRegular]!.size - let _ = panelSubtitleNodeLayout[TitleNodeStateRegular]!.size - let _ = nextPanelSubtitleNodeLayout[TitleNodeStateRegular]!.size - let usernameSize = usernameNodeLayout[TitleNodeStateRegular]!.size - - var titleHorizontalOffset: CGFloat = 0.0 - if let credibilityIconSize = self.credibilityIconSize, let titleExpandedCredibilityIconSize = self.titleExpandedCredibilityIconSize { - titleHorizontalOffset = -(credibilityIconSize.width + 4.0) / 2.0 - - var collapsedTransitionOffset: CGFloat = 0.0 - if let navigationTransition = self.navigationTransition { - collapsedTransitionOffset = -10.0 * navigationTransition.fraction - } - - transition.updateFrame(view: self.titleCredibilityIconView, frame: CGRect(origin: CGPoint(x: titleSize.width + 4.0 + collapsedTransitionOffset, y: floor((titleSize.height - credibilityIconSize.height) / 2.0) + 2.0), size: credibilityIconSize)) - transition.updateFrame(view: self.titleExpandedCredibilityIconView, frame: CGRect(origin: CGPoint(x: titleExpandedSize.width + 4.0, y: floor((titleExpandedSize.height - titleExpandedCredibilityIconSize.height) / 2.0) + 1.0), size: titleExpandedCredibilityIconSize)) - } - - var titleFrame: CGRect - var subtitleFrame: CGRect - let usernameFrame: CGRect - let usernameSpacing: CGFloat = 4.0 - - transition.updateFrame(node: self.avatarListNode.listContainerNode.bottomShadowNode, frame: CGRect(origin: CGPoint(x: 0.0, y: expandedAvatarHeight - 70.0), size: CGSize(width: width, height: 70.0))) - - if self.isAvatarExpanded { - let minTitleSize = CGSize(width: titleSize.width * 0.7, height: titleSize.height * 0.7) - let minTitleFrame = CGRect(origin: CGPoint(x: 16.0, y: expandedAvatarHeight - 58.0 - UIScreenPixel + (subtitleSize.height.isZero ? 10.0 : 0.0)), size: minTitleSize) - - titleFrame = CGRect(origin: CGPoint(x: minTitleFrame.midX - titleSize.width / 2.0, y: minTitleFrame.midY - titleSize.height / 2.0), size: titleSize) - subtitleFrame = CGRect(origin: CGPoint(x: 16.0, y: minTitleFrame.maxY + 2.0), size: subtitleSize) - usernameFrame = CGRect(origin: CGPoint(x: width - usernameSize.width - 16.0, y: minTitleFrame.midY - usernameSize.height / 2.0), size: usernameSize) - } else { - titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 9.0 + (subtitleSize.height.isZero ? 11.0 : 0.0)), size: titleSize) - - let totalSubtitleWidth = subtitleSize.width + usernameSpacing + usernameSize.width - if usernameSize.width == 0.0 { - subtitleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize) - usernameFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - usernameSize.width) / 2.0), y: subtitleFrame.maxY + 1.0), size: usernameSize) - } else { - subtitleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - totalSubtitleWidth) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize) - usernameFrame = CGRect(origin: CGPoint(x: subtitleFrame.maxX + usernameSpacing, y: titleFrame.maxY + 1.0), size: usernameSize) - } - } - - let singleTitleLockOffset: CGFloat = (peer?.id == self.context.account.peerId || subtitleSize.height.isZero) ? 8.0 : 0.0 - - let titleLockOffset: CGFloat = 7.0 + singleTitleLockOffset - let titleMaxLockOffset: CGFloat = 7.0 - var titleCollapseOffset = titleFrame.midY - statusBarHeight - titleLockOffset - if case .regular = metrics.widthClass, !isSettings { - titleCollapseOffset -= 7.0 - } - let titleOffset = -min(titleCollapseOffset, contentOffset) - let titleCollapseFraction = max(0.0, min(1.0, contentOffset / titleCollapseOffset)) - - let titleMinScale: CGFloat = 0.6 - let subtitleMinScale: CGFloat = 0.8 - let avatarMinScale: CGFloat = 0.55 - - let apparentTitleLockOffset = (1.0 - titleCollapseFraction) * 0.0 + titleCollapseFraction * titleMaxLockOffset - - let paneAreaExpansionDistance: CGFloat = 32.0 - let effectiveAreaExpansionFraction: CGFloat - if state.isEditing { - effectiveAreaExpansionFraction = 0.0 - } else if isSettings { - var paneAreaExpansionDelta = (self.frame.maxY - navigationHeight) - contentOffset - paneAreaExpansionDelta = max(0.0, min(paneAreaExpansionDelta, paneAreaExpansionDistance)) - effectiveAreaExpansionFraction = 1.0 - paneAreaExpansionDelta / paneAreaExpansionDistance - } else { - var paneAreaExpansionDelta = (paneContainerY - navigationHeight) - contentOffset - paneAreaExpansionDelta = max(0.0, min(paneAreaExpansionDelta, paneAreaExpansionDistance)) - effectiveAreaExpansionFraction = 1.0 - paneAreaExpansionDelta / paneAreaExpansionDistance - } - - let secondarySeparatorAlpha = 1.0 - effectiveAreaExpansionFraction - if self.navigationTransition == nil && !self.isSettings && effectiveSeparatorAlpha == 1.0 && secondarySeparatorAlpha < 1.0 { - effectiveSeparatorAlpha = secondarySeparatorAlpha - } - transition.updateAlpha(node: self.separatorNode, alpha: effectiveSeparatorAlpha) - - self.titleNode.update(stateFractions: [ - TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0, - TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0 - ], transition: transition) - - let subtitleAlpha: CGFloat - var subtitleOffset: CGFloat = 0.0 - let panelSubtitleAlpha: CGFloat - var panelSubtitleOffset: CGFloat = 0.0 - if self.isSettings { - subtitleAlpha = 1.0 - titleCollapseFraction - panelSubtitleAlpha = 0.0 - } else { - if (panelSubtitleString?.text ?? subtitleStringText) != subtitleStringText { - subtitleAlpha = 1.0 - effectiveAreaExpansionFraction - panelSubtitleAlpha = effectiveAreaExpansionFraction - subtitleOffset = -effectiveAreaExpansionFraction * 5.0 - panelSubtitleOffset = (1.0 - effectiveAreaExpansionFraction) * 5.0 - } else { - subtitleAlpha = 1.0 - panelSubtitleAlpha = 0.0 - } - } - self.subtitleNode.update(stateFractions: [ - TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0, - TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0 - ], alpha: subtitleAlpha, transition: transition) - - self.panelSubtitleNode.update(stateFractions: [ - TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0, - TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0 - ], alpha: panelSubtitleAlpha, transition: transition) - - self.nextPanelSubtitleNode.update(stateFractions: [ - TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0, - TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0 - ], alpha: panelSubtitleAlpha, transition: transition) - - self.usernameNode.update(stateFractions: [ - TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0, - TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0 - ], alpha: subtitleAlpha, transition: transition) - - let avatarScale: CGFloat - let avatarOffset: CGFloat - if self.navigationTransition != nil { - if let transitionSourceAvatarFrame = transitionSourceAvatarFrame { - var trueAvatarSize = transitionSourceAvatarFrame.size - if let storyStats = self.avatarListNode.avatarContainerNode.avatarNode.storyStats, storyStats.unseenCount != 0 { - trueAvatarSize.width -= 1.33 * 4.0 - trueAvatarSize.height -= 1.33 * 4.0 - } - - avatarScale = ((1.0 - transitionFraction) * avatarFrame.width + transitionFraction * trueAvatarSize.width) / avatarFrame.width - } else { - avatarScale = 1.0 - } - avatarOffset = 0.0 - } else { - avatarScale = 1.0 * (1.0 - titleCollapseFraction) + avatarMinScale * titleCollapseFraction - avatarOffset = apparentTitleLockOffset + 0.0 * (1.0 - titleCollapseFraction) + 10.0 * titleCollapseFraction - } - - if let previousAvatarView = self.navigationTransition?.previousAvatarView, let transitionSourceAvatarFrame { - let previousScale = ((1.0 - transitionFraction) * avatarFrame.width + transitionFraction * transitionSourceAvatarFrame.width) / transitionSourceAvatarFrame.width - - transition.updateAlpha(layer: previousAvatarView.layer, alpha: transitionFraction) - transition.updateTransformScale(layer: previousAvatarView.layer, scale: previousScale) - transition.updatePosition(layer: previousAvatarView.layer, position: self.view.convert(CGPoint(x: avatarCenter.x - (27.0 * (1.0 - transitionFraction) + 10 * transitionFraction), y: avatarCenter.y - (2.66 * (1.0 - transitionFraction) + 1.0 * transitionFraction)), to: previousAvatarView.superview)) - } - - if subtitleIsButton { - subtitleFrame.origin.y += 11.0 * (1.0 - titleCollapseFraction) - if let subtitleBackgroundButton = self.subtitleBackgroundButton { - transition.updateAlpha(node: subtitleBackgroundButton, alpha: (1.0 - titleCollapseFraction)) - } - if let subtitleBackgroundNode = self.subtitleBackgroundNode { - transition.updateAlpha(node: subtitleBackgroundNode, alpha: (1.0 - titleCollapseFraction)) - } - if let subtitleArrowNode = self.subtitleArrowNode { - transition.updateAlpha(node: subtitleArrowNode, alpha: (1.0 - titleCollapseFraction)) - } - } - - let avatarCornerRadius: CGFloat = isForum ? floor(avatarSize * 0.25) : avatarSize / 2.0 - - if self.isAvatarExpanded { - self.avatarListNode.listContainerNode.isHidden = false - if let transitionSourceAvatarFrame = transitionSourceAvatarFrame { - var trueAvatarSize = transitionSourceAvatarFrame.size - if let storyStats = self.avatarListNode.avatarContainerNode.avatarNode.storyStats, storyStats.unseenCount != 0 { - trueAvatarSize.width -= 1.33 * 4.0 - trueAvatarSize.height -= 1.33 * 4.0 - } - - transition.updateCornerRadius(node: self.avatarListNode.listContainerNode, cornerRadius: transitionFraction * trueAvatarSize.width / 2.0) - transition.updateCornerRadius(node: self.avatarListNode.listContainerNode.controlsClippingNode, cornerRadius: transitionFraction * trueAvatarSize.width / 2.0) - } else { - transition.updateCornerRadius(node: self.avatarListNode.listContainerNode, cornerRadius: 0.0) - transition.updateCornerRadius(node: self.avatarListNode.listContainerNode.controlsClippingNode, cornerRadius: 0.0) - } - } else if self.avatarListNode.listContainerNode.cornerRadius != avatarCornerRadius { - transition.updateCornerRadius(node: self.avatarListNode.listContainerNode.controlsClippingNode, cornerRadius: avatarCornerRadius) - transition.updateCornerRadius(node: self.avatarListNode.listContainerNode, cornerRadius: avatarCornerRadius, completion: { [weak self] _ in - guard let strongSelf = self else { - return - } - strongSelf.avatarListNode.avatarContainerNode.canAttachVideo = true - strongSelf.avatarListNode.listContainerNode.isHidden = true - if !strongSelf.skipCollapseCompletion { - DispatchQueue.main.async { - strongSelf.avatarListNode.listContainerNode.isCollapsing = false - } - } - }) - } - - self.avatarListNode.update(size: CGSize(), avatarSize: avatarSize, isExpanded: self.isAvatarExpanded, peer: peer, isForum: isForum, threadId: self.forumTopicThreadId, threadInfo: threadData?.info, theme: presentationData.theme, transition: transition) - self.editingContentNode.avatarNode.update(peer: peer, threadData: threadData, chatLocation: self.chatLocation, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing) - self.avatarOverlayNode.update(peer: peer, threadData: threadData, chatLocation: self.chatLocation, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing) - if additive { - transition.updateSublayerTransformScaleAdditive(node: self.avatarListNode.avatarContainerNode, scale: avatarScale) - transition.updateSublayerTransformScaleAdditive(node: self.avatarOverlayNode, scale: avatarScale) - } else { - transition.updateSublayerTransformScale(node: self.avatarListNode.avatarContainerNode, scale: avatarScale) - transition.updateSublayerTransformScale(node: self.avatarOverlayNode, scale: avatarScale) - } - - if let avatarStoryView = self.avatarListNode.avatarContainerNode.avatarStoryView?.view { - transition.updateAlpha(layer: avatarStoryView.layer, alpha: 1.0 - transitionFraction) - } - - var apparentAvatarFrame: CGRect - let controlsClippingFrame: CGRect - if self.isAvatarExpanded { - let expandedAvatarCenter = CGPoint(x: expandedAvatarListSize.width / 2.0, y: expandedAvatarListSize.height / 2.0 - contentOffset / 2.0) - apparentAvatarFrame = CGRect(origin: CGPoint(x: expandedAvatarCenter.x * (1.0 - transitionFraction) + transitionFraction * avatarCenter.x, y: expandedAvatarCenter.y * (1.0 - transitionFraction) + transitionFraction * avatarCenter.y), size: CGSize()) - if let transitionSourceAvatarFrame = transitionSourceAvatarFrame { - var trueAvatarSize = transitionSourceAvatarFrame.size - if let storyStats = self.avatarListNode.avatarContainerNode.avatarNode.storyStats, storyStats.unseenCount != 0 { - trueAvatarSize.width -= 1.33 * 4.0 - trueAvatarSize.height -= 1.33 * 4.0 - } - let trueAvatarFrame = trueAvatarSize.centered(around: transitionSourceAvatarFrame.center) - - let expandedFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: expandedAvatarListSize) - controlsClippingFrame = CGRect(origin: CGPoint(x: transitionFraction * trueAvatarFrame.minX + (1.0 - transitionFraction) * expandedFrame.minX, y: transitionFraction * trueAvatarFrame.minY + (1.0 - transitionFraction) * expandedFrame.minY), size: CGSize(width: transitionFraction * trueAvatarFrame.width + (1.0 - transitionFraction) * expandedFrame.width, height: transitionFraction * trueAvatarFrame.height + (1.0 - transitionFraction) * expandedFrame.height)) - } else { - controlsClippingFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: expandedAvatarListSize) - } - } else { - var trueAvatarSize = avatarFrame.size - if let storyStats = self.avatarListNode.avatarContainerNode.avatarNode.storyStats, storyStats.unseenCount != 0 { - trueAvatarSize.width -= 3.0 * 4.0 - trueAvatarSize.height -= 3.0 * 4.0 - } - apparentAvatarFrame = CGRect(origin: CGPoint(x: avatarCenter.x - trueAvatarSize.width / 2.0, y: -contentOffset + avatarOffset + avatarCenter.y - trueAvatarSize.height / 2.0), size: trueAvatarSize) - controlsClippingFrame = apparentAvatarFrame - } - - let avatarClipOffset: CGFloat = !self.isAvatarExpanded && deviceMetrics.hasDynamicIsland && self.avatarClippingNode.clipsToBounds && !isLandscape ? 48.0 : 0.0 - let clippingNodeTransition = ContainedViewLayoutTransition.immediate - clippingNodeTransition.updateFrame(layer: self.avatarClippingNode.layer, frame: CGRect(origin: CGPoint(x: 0.0, y: avatarClipOffset), size: CGSize(width: width, height: 1000.0))) - clippingNodeTransition.updateSublayerTransformOffset(layer: self.avatarClippingNode.layer, offset: CGPoint(x: 0.0, y: -avatarClipOffset)) - let clippingNodeRadiusTransition = ContainedViewLayoutTransition.animated(duration: 0.15, curve: .easeInOut) - clippingNodeRadiusTransition.updateCornerRadius(node: self.avatarClippingNode, cornerRadius: avatarClipOffset > 0.0 ? width / 2.5 : 0.0) - - transition.updateFrameAdditive(node: self.avatarListNode, frame: CGRect(origin: apparentAvatarFrame.center, size: CGSize())) - transition.updateFrameAdditive(node: self.avatarOverlayNode, frame: CGRect(origin: apparentAvatarFrame.center, size: CGSize())) - - let avatarListContainerFrame: CGRect - let avatarListContainerScale: CGFloat - if self.isAvatarExpanded { - if let transitionSourceAvatarFrame = transitionSourceAvatarFrame { - let neutralAvatarListContainerSize = expandedAvatarListSize - var avatarListContainerSize = CGSize(width: neutralAvatarListContainerSize.width * (1.0 - transitionFraction) + transitionSourceAvatarFrame.width * transitionFraction, height: neutralAvatarListContainerSize.height * (1.0 - transitionFraction) + transitionSourceAvatarFrame.height * transitionFraction) - - if let storyStats = self.avatarListNode.avatarContainerNode.avatarNode.storyStats, storyStats.unseenCount != 0 { - avatarListContainerSize.width -= 1.33 * 5.0 - avatarListContainerSize.height -= 1.33 * 5.0 - } - - avatarListContainerFrame = CGRect(origin: CGPoint(x: -avatarListContainerSize.width / 2.0, y: -avatarListContainerSize.height / 2.0), size: avatarListContainerSize) - } else { - avatarListContainerFrame = CGRect(origin: CGPoint(x: -expandedAvatarListSize.width / 2.0, y: -expandedAvatarListSize.height / 2.0), size: expandedAvatarListSize) - } - avatarListContainerScale = 1.0 + max(0.0, -contentOffset / avatarListContainerFrame.height) - } else { - avatarListContainerFrame = CGRect(origin: CGPoint(x: -apparentAvatarFrame.width / 2.0, y: -apparentAvatarFrame.height / 2.0), size: apparentAvatarFrame.size) - avatarListContainerScale = avatarScale - } - transition.updateFrame(node: self.avatarListNode.listContainerNode, frame: avatarListContainerFrame) - let innerScale = avatarListContainerFrame.height / expandedAvatarListSize.height - let innerDeltaX = (avatarListContainerFrame.width - expandedAvatarListSize.width) / 2.0 - let innerDeltaY = (avatarListContainerFrame.height - expandedAvatarListSize.height) / 2.0 - transition.updateSublayerTransformScale(node: self.avatarListNode.listContainerNode, scale: innerScale) - transition.updateFrameAdditive(node: self.avatarListNode.listContainerNode.contentNode, frame: CGRect(origin: CGPoint(x: innerDeltaX + expandedAvatarListSize.width / 2.0, y: innerDeltaY + expandedAvatarListSize.height / 2.0), size: CGSize())) - - transition.updateFrameAdditive(node: self.avatarListNode.listContainerNode.controlsClippingOffsetNode, frame: CGRect(origin: controlsClippingFrame.center, size: CGSize())) - transition.updateFrame(node: self.avatarListNode.listContainerNode.controlsClippingNode, frame: CGRect(origin: CGPoint(x: -controlsClippingFrame.width / 2.0, y: -controlsClippingFrame.height / 2.0), size: controlsClippingFrame.size)) - transition.updateFrameAdditive(node: self.avatarListNode.listContainerNode.controlsContainerNode, frame: CGRect(origin: CGPoint(x: -controlsClippingFrame.minX, y: -controlsClippingFrame.minY), size: CGSize(width: expandedAvatarListSize.width, height: expandedAvatarListSize.height))) - - transition.updateFrame(node: self.avatarListNode.listContainerNode.topShadowNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: expandedAvatarListSize.width, height: navigationHeight + 20.0))) - transition.updateFrame(node: self.avatarListNode.listContainerNode.stripContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: statusBarHeight < 25.0 ? (statusBarHeight + 2.0) : (statusBarHeight - 3.0)), size: CGSize(width: expandedAvatarListSize.width, height: 2.0))) - transition.updateFrame(node: self.avatarListNode.listContainerNode.highlightContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: expandedAvatarListSize.width, height: expandedAvatarListSize.height))) - transition.updateAlpha(node: self.avatarListNode.listContainerNode.controlsContainerNode, alpha: self.isAvatarExpanded ? (1.0 - transitionFraction) : 0.0) - - if additive { - transition.updateSublayerTransformScaleAdditive(node: self.avatarListNode.listContainerTransformNode, scale: avatarListContainerScale) - } else { - transition.updateSublayerTransformScale(node: self.avatarListNode.listContainerTransformNode, scale: avatarListContainerScale) - } - - if deviceMetrics.hasDynamicIsland && self.forumTopicThreadId == nil && self.navigationTransition == nil && !isLandscape { - let maskValue = max(0.0, min(1.0, contentOffset / 120.0)) - self.avatarListNode.containerNode.view.mask = self.avatarListNode.maskNode.view - if maskValue > 0.03 { - self.avatarListNode.bottomCoverNode.isHidden = false - self.avatarListNode.topCoverNode.isHidden = false - self.avatarListNode.maskNode.backgroundColor = .clear - } else { - self.avatarListNode.bottomCoverNode.isHidden = true - self.avatarListNode.topCoverNode.isHidden = true - self.avatarListNode.maskNode.backgroundColor = .white - } - self.avatarListNode.topCoverNode.update(maskValue) - self.avatarListNode.maskNode.update(maskValue) - self.avatarListNode.bottomCoverNode.backgroundColor = UIColor(white: 0.0, alpha: maskValue) - - self.avatarListNode.listContainerNode.topShadowNode.isHidden = !self.isAvatarExpanded - - var avatarMaskOffset: CGFloat = 0.0 - if contentOffset < 0.0 { - avatarMaskOffset -= contentOffset - } - - self.avatarListNode.maskNode.position = CGPoint(x: 0.0, y: -self.avatarListNode.frame.minY + 48.0 + 85.0 + avatarMaskOffset) - self.avatarListNode.maskNode.bounds = CGRect(origin: .zero, size: CGSize(width: 171.0, height: 171.0)) - - self.avatarListNode.bottomCoverNode.position = self.avatarListNode.maskNode.position - self.avatarListNode.bottomCoverNode.bounds = self.avatarListNode.maskNode.bounds - - self.avatarListNode.topCoverNode.position = self.avatarListNode.maskNode.position - self.avatarListNode.topCoverNode.bounds = self.avatarListNode.maskNode.bounds - } else { - self.avatarListNode.bottomCoverNode.isHidden = true - self.avatarListNode.topCoverNode.isHidden = true - self.avatarListNode.containerNode.view.mask = nil - } - - self.avatarListNode.listContainerNode.update(size: expandedAvatarListSize, peer: peer.flatMap(EnginePeer.init), isExpanded: self.isAvatarExpanded, transition: transition) - if self.avatarListNode.listContainerNode.isCollapsing && !self.ignoreCollapse { - self.avatarListNode.avatarContainerNode.canAttachVideo = false - } - - var panelWithAvatarHeight: CGFloat = 35.0 + avatarSize - if threadData != nil { - panelWithAvatarHeight += 10.0 - } - - let rawHeight: CGFloat - let height: CGFloat - let maxY: CGFloat - if self.isAvatarExpanded { - rawHeight = expandedAvatarHeight - height = max(navigationHeight, rawHeight - contentOffset) - maxY = height - } else { - rawHeight = navigationHeight + panelWithAvatarHeight - height = navigationHeight + max(0.0, panelWithAvatarHeight - contentOffset) - maxY = navigationHeight + panelWithAvatarHeight - contentOffset - } - - let apparentHeight = (1.0 - transitionFraction) * height + transitionFraction * transitionSourceHeight - - if !titleSize.width.isZero && !titleSize.height.isZero { - if self.navigationTransition != nil { - var neutralTitleScale: CGFloat = 1.0 - var neutralSubtitleScale: CGFloat = 1.0 - if self.isAvatarExpanded { - neutralTitleScale = 0.7 - neutralSubtitleScale = 1.0 - } - - let titleScale = (transitionFraction * transitionSourceTitleFrame.height + (1.0 - transitionFraction) * titleFrame.height * neutralTitleScale) / (titleFrame.height) - let subtitleScale = max(0.01, min(10.0, (transitionFraction * transitionSourceSubtitleFrame.height + (1.0 - transitionFraction) * subtitleFrame.height * neutralSubtitleScale) / (subtitleFrame.height))) - - var titleFrame = titleFrame - if !self.isAvatarExpanded { - titleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? 0.0 : titleHorizontalOffset * titleScale, dy: 0.0) - } - - let titleCenter = CGPoint(x: transitionFraction * transitionSourceTitleFrame.midX + (1.0 - transitionFraction) * titleFrame.midX, y: transitionFraction * transitionSourceTitleFrame.midY + (1.0 - transitionFraction) * titleFrame.midY) - let subtitleCenter = CGPoint(x: transitionFraction * transitionSourceSubtitleFrame.midX + (1.0 - transitionFraction) * subtitleFrame.midX, y: transitionFraction * transitionSourceSubtitleFrame.midY + (1.0 - transitionFraction) * subtitleFrame.midY) - - let rawTitleFrame = CGRect(origin: CGPoint(x: titleCenter.x - titleFrame.size.width * neutralTitleScale / 2.0, y: titleCenter.y - titleFrame.size.height * neutralTitleScale / 2.0), size: CGSize(width: titleFrame.size.width * neutralTitleScale, height: titleFrame.size.height * neutralTitleScale)) - self.titleNodeRawContainer.frame = rawTitleFrame - transition.updateFrameAdditiveToCenter(node: self.titleNodeContainer, frame: CGRect(origin: rawTitleFrame.center, size: CGSize())) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(), size: CGSize())) - let rawSubtitleFrame = CGRect(origin: CGPoint(x: subtitleCenter.x - subtitleFrame.size.width / 2.0, y: subtitleCenter.y - subtitleFrame.size.height / 2.0), size: subtitleFrame.size) - self.subtitleNodeRawContainer.frame = rawSubtitleFrame - transition.updateFrameAdditiveToCenter(node: self.subtitleNodeContainer, frame: CGRect(origin: rawSubtitleFrame.center, size: CGSize())) - transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: subtitleOffset), size: CGSize())) - transition.updateFrame(node: self.panelSubtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelSubtitleOffset), size: CGSize())) - transition.updateFrame(node: self.nextPanelSubtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelSubtitleOffset), size: CGSize())) - transition.updateFrame(node: self.usernameNode, frame: CGRect(origin: CGPoint(), size: CGSize())) - transition.updateSublayerTransformScale(node: self.titleNodeContainer, scale: titleScale) - transition.updateSublayerTransformScale(node: self.subtitleNodeContainer, scale: subtitleScale) - transition.updateSublayerTransformScale(node: self.usernameNodeContainer, scale: subtitleScale) - } else { - let titleScale: CGFloat - let subtitleScale: CGFloat - var subtitleOffset: CGFloat = 0.0 - if self.isAvatarExpanded { - titleScale = 0.7 - subtitleScale = 1.0 - } else { - titleScale = (1.0 - titleCollapseFraction) * 1.0 + titleCollapseFraction * titleMinScale - subtitleScale = (1.0 - titleCollapseFraction) * 1.0 + titleCollapseFraction * subtitleMinScale - subtitleOffset = titleCollapseFraction * -2.0 - } - - let rawTitleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? 0.0 : titleHorizontalOffset * titleScale, dy: 0.0) - self.titleNodeRawContainer.frame = rawTitleFrame - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(), size: CGSize())) - let rawSubtitleFrame = subtitleFrame - self.subtitleNodeRawContainer.frame = rawSubtitleFrame - let rawUsernameFrame = usernameFrame - self.usernameNodeRawContainer.frame = rawUsernameFrame - if self.isAvatarExpanded { - transition.updateFrameAdditive(node: self.titleNodeContainer, frame: CGRect(origin: rawTitleFrame.center, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset + apparentTitleLockOffset)) - transition.updateFrameAdditive(node: self.subtitleNodeContainer, frame: CGRect(origin: rawSubtitleFrame.center, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset)) - transition.updateFrameAdditive(node: self.usernameNodeContainer, frame: CGRect(origin: rawUsernameFrame.center, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset)) - } else { - transition.updateFrameAdditiveToCenter(node: self.titleNodeContainer, frame: CGRect(origin: rawTitleFrame.center, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset + apparentTitleLockOffset)) - - var subtitleCenter = rawSubtitleFrame.center - subtitleCenter.x = rawTitleFrame.center.x + (subtitleCenter.x - rawTitleFrame.center.x) * subtitleScale - subtitleCenter.y += subtitleOffset - transition.updateFrameAdditiveToCenter(node: self.subtitleNodeContainer, frame: CGRect(origin: subtitleCenter, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset)) - - var usernameCenter = rawUsernameFrame.center - usernameCenter.x = rawTitleFrame.center.x + (usernameCenter.x - rawTitleFrame.center.x) * subtitleScale - transition.updateFrameAdditiveToCenter(node: self.usernameNodeContainer, frame: CGRect(origin: usernameCenter, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset)) - } - transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: subtitleOffset), size: CGSize())) - transition.updateFrame(node: self.panelSubtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelSubtitleOffset), size: CGSize())) - transition.updateFrame(node: self.nextPanelSubtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelSubtitleOffset), size: CGSize())) - transition.updateFrame(node: self.usernameNode, frame: CGRect(origin: CGPoint(), size: CGSize())) - transition.updateSublayerTransformScaleAdditive(node: self.titleNodeContainer, scale: titleScale) - transition.updateSublayerTransformScaleAdditive(node: self.subtitleNodeContainer, scale: subtitleScale) - transition.updateSublayerTransformScaleAdditive(node: self.usernameNodeContainer, scale: subtitleScale) - } - } - - let buttonSpacing: CGFloat = 8.0 - let buttonSideInset = max(16.0, containerInset) - - var actionButtonRightOrigin = CGPoint(x: width - buttonSideInset, y: maxY + 24.0 - navigationHeight - UIScreenPixel) - let actionButtonWidth = (width - buttonSideInset * 2.0 + buttonSpacing) / CGFloat(actionButtonKeys.count) - buttonSpacing - let actionButtonSize = CGSize(width: actionButtonWidth, height: 40.0) - - for buttonKey in actionButtonKeys.reversed() { - let buttonNode: PeerInfoHeaderActionButtonNode - var wasAdded = false - if let current = self.actionButtonNodes[buttonKey] { - buttonNode = current - } else { - wasAdded = true - buttonNode = PeerInfoHeaderActionButtonNode(key: buttonKey, action: { [weak self] buttonNode, gesture in - self?.actionButtonPressed(buttonNode, gesture: gesture) - }) - self.actionButtonNodes[buttonKey] = buttonNode - self.buttonsContainerNode.addSubnode(buttonNode) - } - - let buttonFrame = CGRect(origin: CGPoint(x: actionButtonRightOrigin.x - actionButtonSize.width, y: actionButtonRightOrigin.y), size: actionButtonSize) - let buttonTransition: ContainedViewLayoutTransition = wasAdded ? .immediate : transition - - if additive { - buttonTransition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) - } else { - buttonTransition.updateFrame(node: buttonNode, frame: buttonFrame) - } - let buttonText: String - switch buttonKey { - case .message: - buttonText = "Message" - case .addContact: - buttonText = "Add" - default: - fatalError() - } - - buttonNode.update(size: buttonFrame.size, text: buttonText, presentationData: presentationData, transition: buttonTransition) - - if wasAdded { - buttonNode.alpha = 0.0 - } - transition.updateAlpha(node: buttonNode, alpha: 1.0) - actionButtonRightOrigin.x -= actionButtonSize.width + buttonSpacing - } - - for key in self.actionButtonNodes.keys { - if !actionButtonKeys.contains(key) { - if let buttonNode = self.actionButtonNodes[key] { - self.actionButtonNodes.removeValue(forKey: key) - transition.updateAlpha(node: buttonNode, alpha: 0.0) { [weak buttonNode] _ in - buttonNode?.removeFromSupernode() - } - } - } - } - - var buttonRightOrigin = CGPoint(x: width - buttonSideInset, y: maxY + 24.0 - navigationHeight - UIScreenPixel) - if !actionButtonKeys.isEmpty { - buttonRightOrigin.y += actionButtonSize.height + 24.0 - } - let buttonWidth = (width - buttonSideInset * 2.0 + buttonSpacing) / CGFloat(buttonKeys.count) - buttonSpacing - let buttonSize = CGSize(width: buttonWidth, height: 58.0) - - for buttonKey in buttonKeys.reversed() { - let buttonNode: PeerInfoHeaderButtonNode - var wasAdded = false - if let current = self.buttonNodes[buttonKey] { - buttonNode = current - } else { - wasAdded = true - buttonNode = PeerInfoHeaderButtonNode(key: buttonKey, action: { [weak self] buttonNode, gesture in - self?.buttonPressed(buttonNode, gesture: gesture) - }) - self.buttonNodes[buttonKey] = buttonNode - self.buttonsContainerNode.addSubnode(buttonNode) - } - - let buttonFrame = CGRect(origin: CGPoint(x: buttonRightOrigin.x - buttonSize.width, y: buttonRightOrigin.y), size: buttonSize) - let buttonTransition: ContainedViewLayoutTransition = wasAdded ? .immediate : transition - - if additive { - buttonTransition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) - } else { - buttonTransition.updateFrame(node: buttonNode, frame: buttonFrame) - } - let buttonText: String - let buttonIcon: PeerInfoHeaderButtonIcon - switch buttonKey { - case .message: - buttonText = presentationData.strings.PeerInfo_ButtonMessage - buttonIcon = .message - case .discussion: - buttonText = presentationData.strings.PeerInfo_ButtonDiscuss - buttonIcon = .message - case .call: - buttonText = presentationData.strings.PeerInfo_ButtonCall - buttonIcon = .call - case .videoCall: - buttonText = presentationData.strings.PeerInfo_ButtonVideoCall - buttonIcon = .videoCall - case .voiceChat: - if let channel = peer as? TelegramChannel, case .broadcast = channel.info { - buttonText = presentationData.strings.PeerInfo_ButtonLiveStream - } else { - buttonText = presentationData.strings.PeerInfo_ButtonVoiceChat - } - buttonIcon = .voiceChat - case .mute: - let chatIsMuted = peerInfoIsChatMuted(peer: peer, peerNotificationSettings: peerNotificationSettings, threadNotificationSettings: threadNotificationSettings, globalNotificationSettings: globalNotificationSettings) - if chatIsMuted { - buttonText = presentationData.strings.PeerInfo_ButtonUnmute - buttonIcon = .unmute - } else { - buttonText = presentationData.strings.PeerInfo_ButtonMute - buttonIcon = .mute - } - case .more: - buttonText = presentationData.strings.PeerInfo_ButtonMore - buttonIcon = .more - case .addMember: - buttonText = presentationData.strings.PeerInfo_ButtonAddMember - buttonIcon = .addMember - case .search: - buttonText = presentationData.strings.PeerInfo_ButtonSearch - buttonIcon = .search - case .leave: - buttonText = presentationData.strings.PeerInfo_ButtonLeave - buttonIcon = .leave - case .stop: - buttonText = presentationData.strings.PeerInfo_ButtonStop - buttonIcon = .stop - case .addContact: - fatalError() - } - - var isActive = true - if let highlightedButton = state.highlightedButton { - isActive = buttonKey == highlightedButton - } - - buttonNode.update(size: buttonFrame.size, text: buttonText, icon: buttonIcon, isActive: isActive, presentationData: presentationData, transition: buttonTransition) - - if wasAdded { - buttonNode.alpha = 0.0 - } - transition.updateAlpha(node: buttonNode, alpha: 1.0) - - if case .mute = buttonKey, buttonNode.containerNode.alpha.isZero, additive { - if case let .animated(duration, curve) = transition { - ContainedViewLayoutTransition.animated(duration: duration * 0.3, curve: curve).updateAlpha(node: buttonNode.containerNode, alpha: 1.0) - } else { - transition.updateAlpha(node: buttonNode.containerNode, alpha: 1.0) - } - } else { - transition.updateAlpha(node: buttonNode.containerNode, alpha: 1.0) - } - buttonRightOrigin.x -= buttonSize.width + buttonSpacing - } - - for key in self.buttonNodes.keys { - if !buttonKeys.contains(key) { - if let buttonNode = self.buttonNodes[key] { - self.buttonNodes.removeValue(forKey: key) - transition.updateAlpha(node: buttonNode, alpha: 0.0) { [weak buttonNode] _ in - buttonNode?.removeFromSupernode() - } - } - } - } - - let resolvedRegularHeight: CGFloat - if self.isAvatarExpanded { - resolvedRegularHeight = expandedAvatarListSize.height - } else { - resolvedRegularHeight = panelWithAvatarHeight + navigationHeight - } - - let backgroundFrame: CGRect - let separatorFrame: CGRect - - var resolvedHeight: CGFloat - - if state.isEditing { - resolvedHeight = editingContentHeight - backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -2000.0 + max(navigationHeight, resolvedHeight - contentOffset)), size: CGSize(width: width, height: 2000.0)) - separatorFrame = CGRect(origin: CGPoint(x: 0.0, y: max(navigationHeight, resolvedHeight - contentOffset)), size: CGSize(width: width, height: UIScreenPixel)) - } else { - resolvedHeight = resolvedRegularHeight - backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -2000.0 + apparentHeight), size: CGSize(width: width, height: 2000.0)) - separatorFrame = CGRect(origin: CGPoint(x: 0.0, y: apparentHeight), size: CGSize(width: width, height: UIScreenPixel)) - } - - transition.updateFrame(node: self.regularContentNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: resolvedHeight))) - transition.updateFrame(node: self.buttonsContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationHeight + UIScreenPixel), size: CGSize(width: width, height: resolvedHeight - navigationHeight + 500.0))) - - if additive { - transition.updateFrameAdditive(node: self.backgroundNode, frame: backgroundFrame) - self.backgroundNode.update(size: self.backgroundNode.bounds.size, transition: transition) - transition.updateFrameAdditive(node: self.expandedBackgroundNode, frame: backgroundFrame) - self.expandedBackgroundNode.update(size: self.expandedBackgroundNode.bounds.size, transition: transition) - transition.updateFrameAdditive(node: self.separatorNode, frame: separatorFrame) - } else { - transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) - self.backgroundNode.update(size: self.backgroundNode.bounds.size, transition: transition) - transition.updateFrame(node: self.expandedBackgroundNode, frame: backgroundFrame) - self.expandedBackgroundNode.update(size: self.expandedBackgroundNode.bounds.size, transition: transition) - transition.updateFrame(node: self.separatorNode, frame: separatorFrame) - } - - if !state.isEditing && !isSettings { - resolvedHeight += 71.0 - - if !actionButtonKeys.isEmpty { - resolvedHeight += 64.0 - } - } - - if isFirstTime { - self.updateAvatarMask(transition: .immediate) - } - - return resolvedHeight - } - - private func buttonPressed(_ buttonNode: PeerInfoHeaderButtonNode, gesture: ContextGesture?) { - self.performButtonAction?(buttonNode.key, gesture) - } - - private func actionButtonPressed(_ buttonNode: PeerInfoHeaderActionButtonNode, gesture: ContextGesture?) { - self.performButtonAction?(buttonNode.key, gesture) - } - - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - guard let result = super.hitTest(point, with: event) else { - return nil - } - if !self.backgroundNode.frame.contains(point) { - return nil - } - - let setByFrame = self.avatarListNode.listContainerNode.setByYouNode.view.convert(self.avatarListNode.listContainerNode.setByYouNode.bounds, to: self.view).insetBy(dx: -44.0, dy: 0.0) - if self.avatarListNode.listContainerNode.setByYouNode.alpha > 0.0, setByFrame.contains(point) { - return self.avatarListNode.listContainerNode.setByYouNode.view - } - - if !(self.state?.isEditing ?? false) { - switch self.currentCredibilityIcon { - case .premium, .emojiStatus: - let iconFrame = self.titleCredibilityIconView.convert(self.titleCredibilityIconView.bounds, to: self.view) - let expandedIconFrame = self.titleExpandedCredibilityIconView.convert(self.titleExpandedCredibilityIconView.bounds, to: self.view) - if expandedIconFrame.contains(point) && self.isAvatarExpanded { - return self.titleExpandedCredibilityIconView.hitTest(self.view.convert(point, to: self.titleExpandedCredibilityIconView), with: event) - } else if iconFrame.contains(point) { - return self.titleCredibilityIconView.hitTest(self.view.convert(point, to: self.titleCredibilityIconView), with: event) - } - default: - break - } - } - - if let subtitleBackgroundButton = self.subtitleBackgroundButton, subtitleBackgroundButton.view.convert(subtitleBackgroundButton.bounds, to: self.view).contains(point) { - if let result = subtitleBackgroundButton.view.hitTest(self.view.convert(point, to: subtitleBackgroundButton.view), with: event) { - return result - } - } - - if result.isDescendant(of: self.navigationButtonContainer.view) { - return result - } - - if result == self.view || result == self.regularContentNode.view || result == self.editingContentNode.view { - return nil - } - - return result - } - - func updateIsAvatarExpanded(_ isAvatarExpanded: Bool, transition: ContainedViewLayoutTransition) { - if self.isAvatarExpanded != isAvatarExpanded { - self.isAvatarExpanded = isAvatarExpanded - if isAvatarExpanded { - self.avatarListNode.listContainerNode.selectFirstItem() - } - if case .animated = transition, !isAvatarExpanded { - self.avatarListNode.animateAvatarCollapse(transition: transition) - } - - self.updateAvatarMask(transition: transition) - } - } - - private func updateAvatarMask(transition: ContainedViewLayoutTransition) { - guard let (width, deviceMetrics) = self.validLayout, deviceMetrics.hasDynamicIsland else { - return - } - let maskScale: CGFloat = isAvatarExpanded ? width / 100.0 : 1.0 - transition.updateTransformScale(layer: self.avatarListNode.maskNode.layer, scale: maskScale) - transition.updateTransformScale(layer: self.avatarListNode.bottomCoverNode.layer, scale: maskScale) - transition.updateTransformScale(layer: self.avatarListNode.topCoverNode.layer, scale: maskScale) - - let maskAnchorPoint = CGPoint(x: 0.5, y: isAvatarExpanded ? 0.37 : 0.5) - transition.updateAnchorPoint(layer: self.avatarListNode.maskNode.layer, anchorPoint: maskAnchorPoint) - } -} - -private class DynamicIslandMaskNode: ASDisplayNode { - var animationNode: AnimationNode? - - var isForum = false { - didSet { - if self.isForum != oldValue { - self.animationNode?.removeFromSupernode() - let animationNode = AnimationNode(animation: "ForumAvatarMask") - self.addSubnode(animationNode) - self.animationNode = animationNode - } - } - } - - override init() { - let animationNode = AnimationNode(animation: "UserAvatarMask") - self.animationNode = animationNode - - super.init() - - self.addSubnode(animationNode) - } - - func update(_ value: CGFloat) { - self.animationNode?.setProgress(value) - } - - var animating = false - - override func layout() { - self.animationNode?.frame = self.bounds - } -} - -private class DynamicIslandBlurNode: ASDisplayNode { - private var effectView: UIVisualEffectView? - private let fadeNode = ASDisplayNode() - let gradientNode = ASImageNode() - - private var hierarchyTrackingNode: HierarchyTrackingNode? - - deinit { - self.animator?.stopAnimation(true) - } - - override func didLoad() { - super.didLoad() - - let hierarchyTrackingNode = HierarchyTrackingNode({ [weak self] value in - if !value { - self?.animator?.stopAnimation(true) - self?.animator = nil - } - }) - self.hierarchyTrackingNode = hierarchyTrackingNode - self.addSubnode(hierarchyTrackingNode) - - self.fadeNode.backgroundColor = .black - self.fadeNode.alpha = 0.0 - - self.gradientNode.displaysAsynchronously = false - let gradientImage = generateImage(CGSize(width: 100.0, height: 100.0), rotatedContext: { size, context in - let bounds = CGRect(origin: .zero, size: size) - context.clear(bounds) - - var locations: [CGFloat] = [0.0, 0.87, 1.0] - let colors: [CGColor] = [UIColor(rgb: 0x000000, alpha: 0.0).cgColor, UIColor(rgb: 0x000000, alpha: 0.0).cgColor, UIColor(rgb: 0x000000, alpha: 1.0).cgColor] - let colorSpace = CGColorSpaceCreateDeviceRGB() - let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)! - - let endRadius: CGFloat = 90.0 - let center = CGPoint(x: size.width / 2.0, y: size.height / 2.0 + 38.0) - context.drawRadialGradient(gradient, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: endRadius, options: .drawsAfterEndLocation) - }) - self.gradientNode.image = gradientImage - - let effectView = UIVisualEffectView(effect: nil) - self.effectView = effectView - self.view.insertSubview(effectView, at: 0) - - self.addSubnode(self.gradientNode) - self.addSubnode(self.fadeNode) - } - - private var animator: UIViewPropertyAnimator? - - func prepare() -> Bool { - guard self.animator == nil else { - return false - } - let animator = UIViewPropertyAnimator(duration: 1.0, curve: .linear) - self.animator = animator - self.effectView?.effect = nil - animator.addAnimations { [weak self] in - self?.effectView?.effect = UIBlurEffect(style: .dark) - } - return true - } - - func update(_ value: CGFloat) { - let fadeAlpha = min(1.0, max(0.0, -0.25 + value * 1.55)) - if value > 0.0 { - var value = value - let updated = self.prepare() - if value > 0.99 && updated { - value = 0.99 - } - self.animator?.fractionComplete = max(0.0, -0.1 + value * 1.1) - } else { - self.animator?.stopAnimation(true) - self.animator = nil - self.effectView?.effect = nil - } - self.fadeNode.alpha = fadeAlpha - } - - override func layout() { - super.layout() - - self.effectView?.frame = self.bounds - self.fadeNode.frame = self.bounds - - let gradientSize = CGSize(width: 100.0, height: 100.0) - self.gradientNode.frame = CGRect(origin: CGPoint(x: (self.bounds.width - gradientSize.width) / 2.0, y: 0.0), size: gradientSize) - } -} diff --git a/submodules/TelegramUI/Sources/PollResultsController.swift b/submodules/TelegramUI/Sources/PollResultsController.swift index ebbe1b6164d..a634c29e409 100644 --- a/submodules/TelegramUI/Sources/PollResultsController.swift +++ b/submodules/TelegramUI/Sources/PollResultsController.swift @@ -251,7 +251,7 @@ private func pollResultsControllerEntries(presentationData: PresentationData, po displayCount = Int(voterCount) } for peerIndex in 0 ..< displayCount { - let fakeUser = TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil) + let fakeUser = TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) let peer = EngineRenderedPeer(peer: EnginePeer(fakeUser)) entries.append(.optionPeer(optionId: i, index: peerIndex, peer: peer, optionText: optionTextHeader, optionAdditionalText: optionAdditionalTextHeader, optionCount: voterCount, optionExpanded: false, opaqueIdentifier: option.opaqueIdentifier, shimmeringAlternation: peerIndex % 2, isFirstInOption: peerIndex == 0)) } diff --git a/submodules/TelegramUI/Sources/PreparedChatHistoryViewTransition.swift b/submodules/TelegramUI/Sources/PreparedChatHistoryViewTransition.swift index 21e2e19c5c6..fa0fccb2a1b 100644 --- a/submodules/TelegramUI/Sources/PreparedChatHistoryViewTransition.swift +++ b/submodules/TelegramUI/Sources/PreparedChatHistoryViewTransition.swift @@ -203,7 +203,7 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie if case .center = position, let quote = scrollSubject.quote { position = .center(.custom({ itemNode in if let itemNode = itemNode as? ChatMessageBubbleItemNode { - if let quoteRect = itemNode.getQuoteRect(quote: quote) { + if let quoteRect = itemNode.getQuoteRect(quote: quote.string, offset: quote.offset) { return quoteRect.midY } } diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 1ca659d23f1..65a6ea64892 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -48,6 +48,8 @@ import ChatHistoryEntry import ChatMessageItem import ChatMessageItemImpl import ChatRecentActionsController +import PeerInfoScreen +import ChatQrCodeScreen import NGCore import NGData @@ -61,6 +63,8 @@ private final class AccountUserInterfaceInUseContext { } } +typealias AccountInitialData = (limitsConfiguration: LimitsConfiguration?, contentSettings: ContentSettings?, appConfiguration: AppConfiguration?, availableReplyColors: EngineAvailableColorOptions, availableProfileColors: EngineAvailableColorOptions) + private struct AccountAttributes: Equatable { let sortIndex: Int32 let isTestingEnvironment: Bool @@ -69,12 +73,12 @@ private struct AccountAttributes: Equatable { private enum AddedAccountResult { case upgrading(Float) - case ready(AccountRecordId, Account?, Int32, LimitsConfiguration?, ContentSettings?, AppConfiguration?) + case ready(AccountRecordId, Account?, Int32, AccountInitialData) } private enum AddedAccountsResult { case upgrading(Float) - case ready([(AccountRecordId, Account?, Int32, LimitsConfiguration?, ContentSettings?, AppConfiguration?)]) + case ready([(AccountRecordId, Account?, Int32, AccountInitialData)]) } private var testHasInstance = false @@ -555,15 +559,17 @@ public final class SharedAccountContextImpl: SharedAccountContext { return TelegramEngine(account: account).data.get( TelegramEngine.EngineData.Item.Configuration.Limits(), TelegramEngine.EngineData.Item.Configuration.ContentSettings(), - TelegramEngine.EngineData.Item.Configuration.App() + TelegramEngine.EngineData.Item.Configuration.App(), + TelegramEngine.EngineData.Item.Configuration.AvailableColorOptions(scope: .replies), + TelegramEngine.EngineData.Item.Configuration.AvailableColorOptions(scope: .profile) ) - |> map { limitsConfiguration, contentSettings, appConfiguration -> AddedAccountResult in - return .ready(id, account, attributes.sortIndex, limitsConfiguration._asLimits(), contentSettings, appConfiguration) + |> map { limitsConfiguration, contentSettings, appConfiguration, availableReplyColors, availableProfileColors -> AddedAccountResult in + return .ready(id, account, attributes.sortIndex, (limitsConfiguration._asLimits(), contentSettings, appConfiguration, availableReplyColors, availableProfileColors)) } case let .upgrading(progress): return .single(.upgrading(progress)) default: - return .single(.ready(id, nil, attributes.sortIndex, nil, nil, nil)) + return .single(.ready(id, nil, attributes.sortIndex, (nil, nil, nil, EngineAvailableColorOptions(hash: 0, options: []), EngineAvailableColorOptions(hash: 0, options: [])))) } }) } @@ -584,13 +590,13 @@ public final class SharedAccountContextImpl: SharedAccountContext { let mappedAddedAccounts = combineLatest(queue: .mainQueue(), addedSignals) |> map { results -> AddedAccountsResult in - var readyAccounts: [(AccountRecordId, Account?, Int32, LimitsConfiguration?, ContentSettings?, AppConfiguration?)] = [] + var readyAccounts: [(AccountRecordId, Account?, Int32, AccountInitialData)] = [] var totalProgress: Float = 0.0 var hasItemsWithProgress = false for result in results { switch result { - case let .ready(id, account, sortIndex, limitsConfiguration, contentSettings, appConfiguration): - readyAccounts.append((id, account, sortIndex, limitsConfiguration, contentSettings, appConfiguration)) + case let .ready(id, account, sortIndex, initialData): + readyAccounts.append((id, account, sortIndex, initialData)) totalProgress += 1.0 case let .upgrading(progress): hasItemsWithProgress = true @@ -608,7 +614,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { |> deliverOnMainQueue).start(next: { mappedAddedAccounts, authAccount in print("SharedAccountContextImpl: accounts processed in \(CFAbsoluteTimeGetCurrent() - startTime)") - var addedAccounts: [(AccountRecordId, Account?, Int32, LimitsConfiguration?, ContentSettings?, AppConfiguration?)] = [] + var addedAccounts: [(AccountRecordId, Account?, Int32, AccountInitialData)] = [] switch mappedAddedAccounts { case let .upgrading(progress): self.displayUpgradeProgress(progress) @@ -635,7 +641,16 @@ public final class SharedAccountContextImpl: SharedAccountContext { if let account = accountRecord.1 { if existingAccountPeerKeys.contains(AccountPeerKey(peerId: account.peerId, isTestingEnvironment: account.testingEnvironment)) { // MARK: Nicegram DB Changes - let context = AccountContextImpl(sharedContext: self, account: account, limitsConfiguration: accountRecord.3 ?? .defaultValue, contentSettings: accountRecord.4 ?? .default, appConfiguration: accountRecord.5 ?? .defaultValue) + let initialData = accountRecord.3 + let context = AccountContextImpl( + sharedContext: self, + account: account, + limitsConfiguration: initialData.0 ?? .defaultValue, + contentSettings: initialData.1 ?? .default, + appConfiguration: initialData.2 ?? .defaultValue, + availableReplyColors: initialData.3, + availableProfileColors: initialData.4 + ) self.activeAccountsValue?.accounts.append((account.id, context, accountRecord.2)) } else { @@ -646,7 +661,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { assertionFailure() } - let context = AccountContextImpl(sharedContext: self, account: account, limitsConfiguration: accountRecord.3 ?? .defaultValue, contentSettings: accountRecord.4 ?? .default, appConfiguration: accountRecord.5 ?? .defaultValue) + let context = AccountContextImpl(sharedContext: self, account: account, limitsConfiguration: accountRecord.3.limitsConfiguration ?? .defaultValue, contentSettings: accountRecord.3.contentSettings ?? .default, appConfiguration: accountRecord.3.appConfiguration ?? .defaultValue, availableReplyColors: accountRecord.3.availableReplyColors, availableProfileColors: accountRecord.3.availableProfileColors) self.activeAccountsValue!.accounts.append((account.id, context, accountRecord.2)) @@ -1353,7 +1368,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { } public func makeTempAccountContext(account: Account) -> AccountContext { - return AccountContextImpl(sharedContext: self, account: account, limitsConfiguration: .defaultValue, contentSettings: .default, appConfiguration: .defaultValue, temp: true) + return AccountContextImpl(sharedContext: self, account: account, limitsConfiguration: .defaultValue, contentSettings: .default, appConfiguration: .defaultValue, availableReplyColors: EngineAvailableColorOptions(hash: 0, options: []), availableProfileColors: EngineAvailableColorOptions(hash: 0, options: []), temp: true) } public func openChatMessage(_ params: OpenChatMessageParams) -> Bool { @@ -1550,8 +1565,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { return resolveUrlImpl(context: context, peerId: peerId, url: url, skipUrlAuth: skipUrlAuth) } - public func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, forceExternal: Bool, openPeer: @escaping (EnginePeer, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)?, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?, progress: Promise?) { - openResolvedUrlImpl(resolvedUrl, context: context, urlContext: urlContext, navigationController: navigationController, forceExternal: forceExternal, openPeer: openPeer, sendFile: sendFile, sendSticker: sendSticker, requestMessageActionUrlAuth: requestMessageActionUrlAuth, joinVoiceChat: joinVoiceChat, present: present, dismissInput: dismissInput, contentContext: contentContext, progress: progress) + public func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, forceExternal: Bool, openPeer: @escaping (EnginePeer, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)?, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?, progress: Promise?, completion: (() -> Void)?) { + openResolvedUrlImpl(resolvedUrl, context: context, urlContext: urlContext, navigationController: navigationController, forceExternal: forceExternal, openPeer: openPeer, sendFile: sendFile, sendSticker: sendSticker, requestMessageActionUrlAuth: requestMessageActionUrlAuth, joinVoiceChat: joinVoiceChat, present: present, dismissInput: dismissInput, contentContext: contentContext, progress: progress, completion: completion) } public func makeDeviceContactInfoController(context: AccountContext, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController { @@ -1566,6 +1581,33 @@ public final class SharedAccountContextImpl: SharedAccountContext { return ChatControllerImpl(context: context, chatLocation: chatLocation, subject: subject, botStart: botStart, mode: mode) } + public func makeChatHistoryListNode( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal), + chatLocation: ChatLocation, + chatLocationContextHolder: Atomic, + tagMask: MessageTags?, + source: ChatHistoryListSource, + subject: ChatControllerSubject?, + controllerInteraction: ChatControllerInteractionProtocol, + selectedMessages: Signal?, NoError>, + mode: ChatHistoryListMode + ) -> ChatHistoryListNode { + return ChatHistoryListNodeImpl( + context: context, + updatedPresentationData: updatedPresentationData, + chatLocation: chatLocation, + chatLocationContextHolder: chatLocationContextHolder, + tagMask: tagMask, + source: source, + subject: subject, + controllerInteraction: controllerInteraction as! ChatControllerInteraction, + selectedMessages: selectedMessages, + mode: mode, + messageTransitionNode: { return nil } + ) + } + public func makePeerSharedMediaController(context: AccountContext, peerId: PeerId) -> ViewController? { return nil } @@ -1622,7 +1664,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { return presentAddMembersImpl(context: context, updatedPresentationData: updatedPresentationData, parentController: parentController, groupPeer: groupPeer, selectAddMemberDisposable: selectAddMemberDisposable, addMemberDisposable: addMemberDisposable) } - public func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)? = nil, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?, isCentered: Bool) -> ListViewItem { + public func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)? = nil, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?, accountPeer: Peer?, isCentered: Bool) -> ListViewItem { let controllerInteraction: ChatControllerInteraction controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in @@ -1694,6 +1736,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { }, openNoAdsDemo: { }, displayGiveawayParticipationStatus: { _ in }, openPremiumStatusInfo: { _, _, _, _ in + }, openRecommendedChannelContextMenu: { _, _, _ in }, requestMessageUpdate: { _, _ in }, cancelInteractiveKeyboardGestures: { }, dismissTextInput: { @@ -1716,7 +1759,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { chatLocation = .peer(id: messages.first!.id.peerId) } - return ChatMessageItemImpl(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, chatBubbleCorners: chatBubbleCorners, animatedEmojiScale: 1.0, isPreview: true), context: context, chatLocation: chatLocation, associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: false, subject: nil, contactsPeerIds: Set(), animatedEmojiStickers: [:], forcedResourceStatus: forcedResourceStatus, availableReactions: availableReactions, defaultReaction: nil, isPremium: false, accountPeer: nil, forceInlineReactions: true), controllerInteraction: controllerInteraction, content: content, disableDate: true, additionalContent: nil) + return ChatMessageItemImpl(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, chatBubbleCorners: chatBubbleCorners, animatedEmojiScale: 1.0, isPreview: true), context: context, chatLocation: chatLocation, associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: false, subject: nil, contactsPeerIds: Set(), animatedEmojiStickers: [:], forcedResourceStatus: forcedResourceStatus, availableReactions: availableReactions, defaultReaction: nil, isPremium: false, accountPeer: accountPeer.flatMap(EnginePeer.init), forceInlineReactions: true), controllerInteraction: controllerInteraction, content: content, disableDate: true, additionalContent: nil) } public func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader { @@ -1767,8 +1810,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { return recentSessionsController(context: context, activeSessionsContext: activeSessionsContext, webSessionsContext: context.engine.privacy.webSessions(), websitesOnly: false) } - public func makeChatQrCodeScreen(context: AccountContext, peer: Peer, threadId: Int64?) -> ViewController { - return ChatQrCodeScreen(context: context, subject: .peer(peer: peer, threadId: threadId, temporary: false)) + public func makeChatQrCodeScreen(context: AccountContext, peer: Peer, threadId: Int64?, temporary: Bool) -> ViewController { + return ChatQrCodeScreen(context: context, subject: .peer(peer: peer, threadId: threadId, temporary: temporary)) } public func makePrivacyAndSecurityController(context: AccountContext) -> ViewController { @@ -1881,6 +1924,10 @@ public final class SharedAccountContextImpl: SharedAccountContext { mappedSource = .channelBoost(peerId) case .nameColor: mappedSource = .nameColor + case .similarChannels: + mappedSource = .similarChannels + case .wallpapers: + mappedSource = .wallpapers } let controller = PremiumIntroScreen(context: context, source: mappedSource, forceDark: forceDark) controller.wasDismissed = dismissed @@ -1920,6 +1967,10 @@ public final class SharedAccountContextImpl: SharedAccountContext { mappedSubject = .translation case .stories: mappedSubject = .stories + case .colors: + mappedSubject = .colors + case .wallpapers: + mappedSubject = .wallpapers } return PremiumDemoScreen(context: context, subject: mappedSubject, action: action) } @@ -1975,8 +2026,16 @@ public final class SharedAccountContextImpl: SharedAccountContext { return installedStickerPacksController(context: context, mode: mode, forceTheme: forceTheme) } - public func makeChannelStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, peerId: EnginePeer.Id, boosts: Bool, boostStatus: ChannelBoostStatus?, statsDatacenterId: Int32) -> ViewController { - return channelStatsController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, section: boosts ? .boosts : .stats, boostStatus: nil, statsDatacenterId: statsDatacenterId) + public func makeChannelStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, peerId: EnginePeer.Id, boosts: Bool, boostStatus: ChannelBoostStatus?) -> ViewController { + return channelStatsController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, section: boosts ? .boosts : .stats, boostStatus: boostStatus) + } + + public func makeMessagesStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, messageId: EngineMessage.Id) -> ViewController { + return messageStatsController(context: context, updatedPresentationData: updatedPresentationData, subject: .message(id: messageId)) + } + + public func makeStoryStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, peerId: EnginePeer.Id, storyId: Int32, storyItem: EngineStoryItem, fromStory: Bool) -> ViewController { + return messageStatsController(context: context, updatedPresentationData: updatedPresentationData, subject: .story(peerId: peerId, id: storyId, item: storyItem, fromStory: fromStory)) } } @@ -1985,18 +2044,22 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: []) } else if let _ = peer as? TelegramChannel { var forumTopicThread: ChatReplyThreadMessage? + var switchToRecommendedChannels = false switch mode { case let .forumTopic(thread): forumTopicThread = thread + case .recommendedChannels: + switchToRecommendedChannels = true default: break } - return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [], forumTopicThread: forumTopicThread) + return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [], forumTopicThread: forumTopicThread, switchToRecommendedChannels: switchToRecommendedChannels) } else if peer is TelegramUser { var nearbyPeerDistance: Int32? var reactionSourceMessageId: MessageId? var callMessages: [Message] = [] var hintGroupInCommon: PeerId? + switch mode { case let .nearbyPeer(distance): nearbyPeerDistance = distance @@ -2010,6 +2073,8 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation reactionSourceMessageId = messageId case .forumTopic: break + default: + break } return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nearbyPeerDistance, reactionSourceMessageId: reactionSourceMessageId, callMessages: callMessages, hintGroupInCommon: hintGroupInCommon) } else if peer is TelegramSecretChat { diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index 0e4ba4227b5..5a5a2228a90 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -41,6 +41,7 @@ import LocalMediaResources import ImageCompression import TextFormat import MediaEditor +import PeerInfoScreen private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceholderNode { private var presentationData: PresentationData @@ -66,7 +67,7 @@ private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceh self.presentationData = presentationData self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.presentationInterfaceState.limitsConfiguration, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.presentationInterfaceState.accountPeerId, mode: .standard(previewing: false), chatLocation: self.presentationInterfaceState.chatLocation, subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil) - self.wallpaperBackgroundNode.update(wallpaper: presentationData.chatWallpaper) + self.wallpaperBackgroundNode.update(wallpaper: presentationData.chatWallpaper, animated: false) } func updateLayout(size: CGSize, needsTiling: Bool, transition: ContainedViewLayoutTransition) { @@ -220,7 +221,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon detailsPlaceholderNode = current } else { detailsPlaceholderNode = DetailsChatPlaceholderNode(context: self.context) - detailsPlaceholderNode.wallpaperBackgroundNode.update(wallpaper: self.presentationData.chatWallpaper) + detailsPlaceholderNode.wallpaperBackgroundNode.update(wallpaper: self.presentationData.chatWallpaper, animated: false) self.detailsPlaceholderNode = detailsPlaceholderNode } self.updateDetailsPlaceholderNode(detailsPlaceholderNode) @@ -262,9 +263,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon // MARK: Nicegram Assistant if #available(iOS 15.0, *) { let assistantController = NativeControllerWrapper( - controller: AssistantUITgHelper.assistantTab( - close: nil - ), + controller: AssistantUITgHelper.assistantTab(), accountContext: self.context, adjustSafeArea: true ) @@ -441,9 +440,11 @@ public final class TelegramRootController: NavigationController, TelegramRootCon let context = self.context - var storyTarget: Stories.PendingTarget? - var isPeerArchived = false - var updatedTransitionOut: ((Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut?)? + let externalState = MediaEditorTransitionOutExternalState( + storyTarget: nil, + isPeerArchived: false, + transitionOut: nil + ) var presentImpl: ((ViewController) -> Void)? var returnToCameraImpl: (() -> Void)? @@ -463,7 +464,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon } }, transitionOut: { finished in - if let transitionOut = (updatedTransitionOut ?? transitionOut)(finished ? storyTarget : nil, isPeerArchived), let destinationView = transitionOut.destinationView { + if let transitionOut = (externalState.transitionOut ?? transitionOut)(finished ? externalState.storyTarget : nil, externalState.isPeerArchived), let destinationView = transitionOut.destinationView { return CameraScreen.TransitionOut( destinationView: destinationView, destinationRect: transitionOut.destinationRect, @@ -519,10 +520,9 @@ public final class TelegramRootController: NavigationController, TelegramRootCon context: context, subject: subject, customTarget: customTarget, - isEditing: false, transitionIn: transitionIn, transitionOut: { finished, isNew in - if finished, let transitionOut = (updatedTransitionOut ?? transitionOut)(storyTarget, false), let destinationView = transitionOut.destinationView { + if finished, let transitionOut = (externalState.transitionOut ?? transitionOut)(externalState.storyTarget, false), let destinationView = transitionOut.destinationView { return MediaEditorScreen.TransitionOut( destinationView: destinationView, destinationRect: transitionOut.destinationRect, @@ -537,20 +537,20 @@ public final class TelegramRootController: NavigationController, TelegramRootCon } else { return nil } - }, completion: { [weak self] randomId, mediaResult, mediaAreas, caption, options, stickers, commit in - guard let self, let mediaResult else { + }, completion: { [weak self] result, commit in + guard let self else { dismissCameraImpl?() commit({}) return } - + let target: Stories.PendingTarget let targetPeerId: EnginePeer.Id if let customTarget { target = .peer(customTarget) targetPeerId = customTarget } else { - if let sendAsPeerId = options.sendAsPeerId { + if let sendAsPeerId = result.options.sendAsPeerId { target = .peer(sendAsPeerId) targetPeerId = sendAsPeerId } else { @@ -558,8 +558,8 @@ public final class TelegramRootController: NavigationController, TelegramRootCon targetPeerId = context.account.peerId } } - storyTarget = target - + externalState.storyTarget = target + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: targetPeerId)) |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in guard let self, let peer else { @@ -567,120 +567,16 @@ public final class TelegramRootController: NavigationController, TelegramRootCon } if case let .user(user) = peer { - isPeerArchived = user.storiesHidden ?? false + externalState.isPeerArchived = user.storiesHidden ?? false } else if case let .channel(channel) = peer { - isPeerArchived = channel.storiesHidden ?? false + externalState.isPeerArchived = channel.storiesHidden ?? false } - let context = self.context - if let rootTabController = self.rootTabController { - if let index = rootTabController.controllers.firstIndex(where: { $0 is ChatListController}) { - rootTabController.selectedIndex = index - } - } - - let completionImpl: () -> Void = { [weak self] in - guard let self else { - return - } - - var chatListController: ChatListControllerImpl? - - if isPeerArchived { - var viewControllers = self.viewControllers - - let archiveController = ChatListControllerImpl(context: context, location: .chatList(groupId: .archive), controlsHistoryPreload: false, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: false) - updatedTransitionOut = archiveController.storyCameraTransitionOut() - chatListController = archiveController - viewControllers.insert(archiveController, at: 1) - self.setViewControllers(viewControllers, animated: false) - } else { - chatListController = self.chatListController as? ChatListControllerImpl - } - - if let chatListController { - let _ = (chatListController.hasPendingStories - |> filter { $0 } - |> take(1) - |> timeout(isPeerArchived ? 0.5 : 0.25, queue: .mainQueue(), alternate: .single(true)) - |> deliverOnMainQueue).startStandalone(completed: { [weak chatListController] in - guard let chatListController else { - return - } - - chatListController.scrollToStories(peerId: targetPeerId) - Queue.mainQueue().justDispatch { - commit({}) - } - }) - } else { - Queue.mainQueue().justDispatch { - commit({}) - } - } - } - - if let _ = self.chatListController as? ChatListControllerImpl { - switch mediaResult { - case let .image(image, dimensions): - let tempFile = TempBox.shared.tempFile(fileName: "file") - defer { - TempBox.shared.dispose(tempFile) - } - if let imageData = compressImageToJPEG(image, quality: 0.7, tempFilePath: tempFile.path) { - let entities = generateChatInputTextEntities(caption) - Logger.shared.log("MediaEditor", "Calling uploadStory for image, randomId \(randomId)") - let _ = (context.engine.messages.uploadStory(target: target, media: .image(dimensions: dimensions, data: imageData, stickers: stickers), mediaAreas: mediaAreas, text: caption.string, entities: entities, pin: options.pin, privacy: options.privacy, isForwardingDisabled: options.isForwardingDisabled, period: options.timeout, randomId: randomId) - |> deliverOnMainQueue).startStandalone(next: { stableId in - moveStorySource(engine: context.engine, peerId: context.account.peerId, from: randomId, to: Int64(stableId)) - }) - - completionImpl() - } - case let .video(content, firstFrameImage, values, duration, dimensions): - let adjustments: VideoMediaResourceAdjustments - if let valuesData = try? JSONEncoder().encode(values) { - let data = MemoryBuffer(data: valuesData) - let digest = MemoryBuffer(data: data.md5Digest()) - adjustments = VideoMediaResourceAdjustments(data: data, digest: digest, isStory: true) - - let resource: TelegramMediaResource - switch content { - case let .imageFile(path): - resource = LocalFileVideoMediaResource(randomId: Int64.random(in: .min ... .max), path: path, adjustments: adjustments) - case let .videoFile(path): - resource = LocalFileVideoMediaResource(randomId: Int64.random(in: .min ... .max), path: path, adjustments: adjustments) - case let .asset(localIdentifier): - resource = VideoLibraryMediaResource(localIdentifier: localIdentifier, conversion: .compress(adjustments)) - } - let tempFile = TempBox.shared.tempFile(fileName: "file") - defer { - TempBox.shared.dispose(tempFile) - } - let imageData = firstFrameImage.flatMap { compressImageToJPEG($0, quality: 0.6, tempFilePath: tempFile.path) } - let firstFrameFile = imageData.flatMap { data -> TempBoxFile? in - let file = TempBox.shared.tempFile(fileName: "image.jpg") - if let _ = try? data.write(to: URL(fileURLWithPath: file.path)) { - return file - } else { - return nil - } - } - Logger.shared.log("MediaEditor", "Calling uploadStory for video, randomId \(randomId)") - let entities = generateChatInputTextEntities(caption) - let _ = (context.engine.messages.uploadStory(target: target, media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameFile: firstFrameFile, stickers: stickers), mediaAreas: mediaAreas, text: caption.string, entities: entities, pin: options.pin, privacy: options.privacy, isForwardingDisabled: options.isForwardingDisabled, period: options.timeout, randomId: randomId) - |> deliverOnMainQueue).startStandalone(next: { stableId in - moveStorySource(engine: context.engine, peerId: context.account.peerId, from: randomId, to: Int64(stableId)) - }) - - completionImpl() - } - } - } + self.proceedWithStoryUpload(target: target, result: result, existingMedia: nil, forwardInfo: nil, externalState: externalState, commit: commit) dismissCameraImpl?() }) - } as (Int64, MediaEditorScreen.Result?, [MediaArea], NSAttributedString, MediaEditorResultPrivacy, [TelegramMediaFile], @escaping (@escaping () -> Void) -> Void) -> Void + } as (MediaEditorScreen.Result, @escaping (@escaping () -> Void) -> Void) -> Void ) controller.cancelled = { showDraftTooltip in if showDraftTooltip { @@ -735,6 +631,164 @@ public final class TelegramRootController: NavigationController, TelegramRootCon }) } + public func proceedWithStoryUpload(target: Stories.PendingTarget, result: MediaEditorScreenResult, existingMedia: EngineMedia?, forwardInfo: Stories.PendingForwardInfo?, externalState: MediaEditorTransitionOutExternalState, commit: @escaping (@escaping () -> Void) -> Void) { + guard let result = result as? MediaEditorScreen.Result else { + return + } + let context = self.context + let targetPeerId: EnginePeer.Id + switch target { + case let .peer(peerId): + targetPeerId = peerId + case .myStories: + targetPeerId = context.account.peerId + } + + if let rootTabController = self.rootTabController { + if let index = rootTabController.controllers.firstIndex(where: { $0 is ChatListController}) { + rootTabController.selectedIndex = index + } + if forwardInfo != nil { + var viewControllers = self.viewControllers + var dismissNext = false + var range: Range? + for i in (0 ..< viewControllers.count).reversed() { + let controller = viewControllers[i] + if controller is MediaEditorScreen { + dismissNext = true + } + if dismissNext { + if controller !== self.rootTabController { + if let current = range { + range = current.lowerBound - 1 ..< current.upperBound + } else { + range = i ..< i + } + } else { + break + } + } + } + if let range { + viewControllers.removeSubrange(range) + self.setViewControllers(viewControllers, animated: false) + } + } + } + + let completionImpl: () -> Void = { [weak self] in + guard let self else { + return + } + + var chatListController: ChatListControllerImpl? + + if externalState.isPeerArchived { + var viewControllers = self.viewControllers + + let archiveController = ChatListControllerImpl(context: context, location: .chatList(groupId: .archive), controlsHistoryPreload: false, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: false) + externalState.transitionOut = archiveController.storyCameraTransitionOut() + chatListController = archiveController + viewControllers.insert(archiveController, at: 1) + self.setViewControllers(viewControllers, animated: false) + } else { + chatListController = self.chatListController as? ChatListControllerImpl + externalState.transitionOut = chatListController?.storyCameraTransitionOut() + } + + if let chatListController { + let _ = (chatListController.hasPendingStories + |> filter { $0 } + |> take(1) + |> timeout(externalState.isPeerArchived ? 0.5 : 0.25, queue: .mainQueue(), alternate: .single(true)) + |> deliverOnMainQueue).startStandalone(completed: { [weak chatListController] in + guard let chatListController else { + return + } + + chatListController.scrollToStories(peerId: targetPeerId) + Queue.mainQueue().justDispatch { + commit({}) + } + }) + } else { + Queue.mainQueue().justDispatch { + commit({}) + } + } + } + + if let _ = self.chatListController as? ChatListControllerImpl { + var media: EngineStoryInputMedia? + + if let mediaResult = result.media { + switch mediaResult { + case let .image(image, dimensions): + let tempFile = TempBox.shared.tempFile(fileName: "file") + defer { + TempBox.shared.dispose(tempFile) + } + if let imageData = compressImageToJPEG(image, quality: 0.7, tempFilePath: tempFile.path) { + media = .image(dimensions: dimensions, data: imageData, stickers: result.stickers) + } + case let .video(content, firstFrameImage, values, duration, dimensions): + let adjustments: VideoMediaResourceAdjustments + if let valuesData = try? JSONEncoder().encode(values) { + let data = MemoryBuffer(data: valuesData) + let digest = MemoryBuffer(data: data.md5Digest()) + adjustments = VideoMediaResourceAdjustments(data: data, digest: digest, isStory: true) + + let resource: TelegramMediaResource + switch content { + case let .imageFile(path): + resource = LocalFileVideoMediaResource(randomId: Int64.random(in: .min ... .max), path: path, adjustments: adjustments) + case let .videoFile(path): + resource = LocalFileVideoMediaResource(randomId: Int64.random(in: .min ... .max), path: path, adjustments: adjustments) + case let .asset(localIdentifier): + resource = VideoLibraryMediaResource(localIdentifier: localIdentifier, conversion: .compress(adjustments)) + } + let tempFile = TempBox.shared.tempFile(fileName: "file") + defer { + TempBox.shared.dispose(tempFile) + } + let imageData = firstFrameImage.flatMap { compressImageToJPEG($0, quality: 0.6, tempFilePath: tempFile.path) } + let firstFrameFile = imageData.flatMap { data -> TempBoxFile? in + let file = TempBox.shared.tempFile(fileName: "image.jpg") + if let _ = try? data.write(to: URL(fileURLWithPath: file.path)) { + return file + } else { + return nil + } + } + media = .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameFile: firstFrameFile, stickers: result.stickers) + } + } + } else if let existingMedia { + media = .existing(media: existingMedia._asMedia()) + } + + if let media { + let _ = (context.engine.messages.uploadStory( + target: target, + media: media, + mediaAreas: result.mediaAreas, + text: result.caption.string, + entities: generateChatInputTextEntities(result.caption), + pin: result.options.pin, + privacy: result.options.privacy, + isForwardingDisabled: result.options.isForwardingDisabled, + period: result.options.timeout, + randomId: result.randomId, + forwardInfo: forwardInfo + ) + |> deliverOnMainQueue).startStandalone(next: { stableId in + moveStorySource(engine: context.engine, peerId: context.account.peerId, from: result.randomId, to: Int64(stableId)) + }) + } + completionImpl() + } + } + public func openSettings() { guard let rootTabController = self.rootTabController else { return @@ -747,3 +801,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon } } } + +extension MediaEditorScreen.Result: MediaEditorScreenResult { + +} diff --git a/submodules/TelegramUI/Sources/TextLinkHandling.swift b/submodules/TelegramUI/Sources/TextLinkHandling.swift index 0555f41fb05..b02831f29b3 100644 --- a/submodules/TelegramUI/Sources/TextLinkHandling.swift +++ b/submodules/TelegramUI/Sources/TextLinkHandling.swift @@ -54,7 +54,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: EnginePeer.Id?, n sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, - present: presentImpl, dismissInput: {}, contentContext: nil, progress: nil) + present: presentImpl, dismissInput: {}, contentContext: nil, progress: nil, completion: nil) } let openLinkImpl: (String) -> Void = { [weak controller] url in @@ -94,7 +94,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: EnginePeer.Id?, n if let navigationController = controller.navigationController as? NavigationController { openResolvedUrlImpl(result, context: context, urlContext: peerId.flatMap { .chat(peerId: $0, updatedPresentationData: nil) } ?? .generic, navigationController: navigationController, forceExternal: false, openPeer: { peer, navigateToPeer in openResolvedPeerImpl(peer, navigateToPeer) - }, sendFile: nil, sendSticker: nil, joinVoiceChat: nil, present: { c, a in }, dismissInput: {}, contentContext: nil, progress: nil) + }, sendFile: nil, sendSticker: nil, joinVoiceChat: nil, present: { c, a in }, dismissInput: {}, contentContext: nil, progress: nil, completion: nil) } default: break diff --git a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift index c1bc10c8d06..39f77c11ae8 100644 --- a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift +++ b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift @@ -54,6 +54,9 @@ public struct ExperimentalUISettings: Codable, Equatable { public var storiesJpegExperiment: Bool public var crashOnMemoryPressure: Bool public var unidirectionalSwipeToReply: Bool + public var dustEffect: Bool + public var callUIV2: Bool + public var allowWebViewInspection: Bool public static var defaultSettings: ExperimentalUISettings { return ExperimentalUISettings( @@ -86,7 +89,10 @@ public struct ExperimentalUISettings: Codable, Equatable { storiesExperiment: false, storiesJpegExperiment: false, crashOnMemoryPressure: false, - unidirectionalSwipeToReply: false + unidirectionalSwipeToReply: false, + dustEffect: false, + callUIV2: false, + allowWebViewInspection: false ) } @@ -119,7 +125,10 @@ public struct ExperimentalUISettings: Codable, Equatable { storiesExperiment: Bool, storiesJpegExperiment: Bool, crashOnMemoryPressure: Bool, - unidirectionalSwipeToReply: Bool + unidirectionalSwipeToReply: Bool, + dustEffect: Bool, + callUIV2: Bool, + allowWebViewInspection: Bool ) { self.keepChatNavigationStack = keepChatNavigationStack self.skipReadHistory = skipReadHistory @@ -150,6 +159,9 @@ public struct ExperimentalUISettings: Codable, Equatable { self.storiesJpegExperiment = storiesJpegExperiment self.crashOnMemoryPressure = crashOnMemoryPressure self.unidirectionalSwipeToReply = unidirectionalSwipeToReply + self.dustEffect = dustEffect + self.callUIV2 = callUIV2 + self.allowWebViewInspection = allowWebViewInspection } public init(from decoder: Decoder) throws { @@ -184,6 +196,9 @@ public struct ExperimentalUISettings: Codable, Equatable { self.storiesJpegExperiment = try container.decodeIfPresent(Bool.self, forKey: "storiesJpegExperiment") ?? false self.crashOnMemoryPressure = try container.decodeIfPresent(Bool.self, forKey: "crashOnMemoryPressure") ?? false self.unidirectionalSwipeToReply = try container.decodeIfPresent(Bool.self, forKey: "unidirectionalSwipeToReply") ?? false + self.dustEffect = try container.decodeIfPresent(Bool.self, forKey: "dustEffect_2") ?? false + self.callUIV2 = try container.decodeIfPresent(Bool.self, forKey: "callUIV2") ?? false + self.allowWebViewInspection = try container.decodeIfPresent(Bool.self, forKey: "allowWebViewInspection") ?? false } public func encode(to encoder: Encoder) throws { @@ -218,6 +233,9 @@ public struct ExperimentalUISettings: Codable, Equatable { try container.encode(self.storiesJpegExperiment, forKey: "storiesJpegExperiment") try container.encode(self.crashOnMemoryPressure, forKey: "crashOnMemoryPressure") try container.encode(self.unidirectionalSwipeToReply, forKey: "unidirectionalSwipeToReply") + try container.encode(self.dustEffect, forKey: "dustEffect_2") + try container.encode(self.callUIV2, forKey: "callUIV2") + try container.encode(self.allowWebViewInspection, forKey: "allowWebViewInspection") } } diff --git a/submodules/TextFormat/Sources/StringWithAppliedEntities.swift b/submodules/TextFormat/Sources/StringWithAppliedEntities.swift index 7eed41bb7d1..a3949917e95 100644 --- a/submodules/TextFormat/Sources/StringWithAppliedEntities.swift +++ b/submodules/TextFormat/Sources/StringWithAppliedEntities.swift @@ -538,3 +538,41 @@ public func asyncUpdateMessageSyntaxHighlight(engine: TelegramEngine, messageId: } |> runOn(messageSyntaxHighlightQueue) } + +public func asyncStanaloneSyntaxHighlight(current: CachedMessageSyntaxHighlight?, specs: [CachedMessageSyntaxHighlight.Spec]) -> Signal { + if let current, !specs.contains(where: { current.values[$0] == nil }) { + return .single(current) + } + + return Signal { subscriber in + var updated: [CachedMessageSyntaxHighlight.Spec: MessageSyntaxHighlight] = [:] + + let theme = SyntaxterTheme(dark: false, textColor: .black, textFont: internalFixedCodeFont, italicFont: internalFixedCodeFont, mediumFont: internalFixedCodeFont) + + for spec in specs { + if let value = current?.values[spec] { + updated[spec] = value + } else { + var entities: [MessageSyntaxHighlight.Entity] = [] + + if let syntaxHighlighter { + if let highlightedString = syntaxHighlighter.syntax(spec.text, language: spec.language, theme: theme) { + highlightedString.enumerateAttribute(.foregroundColor, in: NSRange(location: 0, length: highlightedString.length), using: { value, subRange, _ in + if let value = value as? UIColor, value != .black { + entities.append(MessageSyntaxHighlight.Entity(color: Int32(bitPattern: value.rgb), range: subRange.lowerBound ..< subRange.upperBound)) + } + }) + } + } + + updated[spec] = MessageSyntaxHighlight(entities: entities) + } + } + + subscriber.putNext(CachedMessageSyntaxHighlight(values: updated)) + subscriber.putCompletion() + + return EmptyDisposable + } + |> runOn(messageSyntaxHighlightQueue) +} diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index 272dfe03416..fe91ca12ae6 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit 272dfe03416f1022dfc5d73875dbe8ace9ec9cfd +Subproject commit fe91ca12ae602fb4685a87ac0955fbb37589e3cb diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.h b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.h index 3f535a84074..224d579d424 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.h +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.h @@ -24,6 +24,11 @@ void applySmoothRoundedCornersImpl(CALayer * _Nonnull layer); @end UIView * _Nullable makePortalView(bool matchPosition); +bool isViewPortalView(UIView * _Nonnull view); +UIView * _Nullable getPortalViewSourceView(UIView * _Nonnull portalView); NSObject * _Nullable makeBlurFilter(); NSObject * _Nullable makeLuminanceToAlphaFilter(); + +void setLayerDisableScreenshots(CALayer * _Nonnull layer, bool disableScreenshots); +void setLayerContentsMaskMode(CALayer * _Nonnull layer, bool maskMode); diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.m b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.m index 3cf8dd34e12..717c5840444 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.m +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.m @@ -171,34 +171,51 @@ void applySmoothRoundedCornersImpl(CALayer * _Nonnull layer) { } UIView * _Nullable makePortalView(bool matchPosition) { - if (@available(iOS 12.0, *)) { - static Class portalViewClass = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - portalViewClass = NSClassFromString([@[@"_", @"UI", @"Portal", @"View"] componentsJoinedByString:@""]); - }); - if (!portalViewClass) { - return nil; - } - UIView *view = [[portalViewClass alloc] init]; - if (!view) { - return nil; - } - - if (@available(iOS 14.0, *)) { - view.forwardsClientHitTestingToSourceView = false; - } - view.matchesPosition = matchPosition; - view.matchesTransform = matchPosition; - view.matchesAlpha = false; - if (@available(iOS 14.0, *)) { - view.allowsHitTesting = false; - } - - return view; + static Class portalViewClass = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + portalViewClass = NSClassFromString([@[@"_", @"UI", @"Portal", @"View"] componentsJoinedByString:@""]); + }); + if (!portalViewClass) { + return nil; + } + UIView *view = [[portalViewClass alloc] init]; + if (!view) { + return nil; + } + + if (@available(iOS 14.0, *)) { + view.forwardsClientHitTestingToSourceView = false; + } + view.matchesPosition = matchPosition; + view.matchesTransform = matchPosition; + view.matchesAlpha = false; + if (@available(iOS 14.0, *)) { + view.allowsHitTesting = false; + } + + return view; +} + +bool isViewPortalView(UIView * _Nonnull view) { + static Class portalViewClass = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + portalViewClass = NSClassFromString([@[@"_", @"UI", @"Portal", @"View"] componentsJoinedByString:@""]); + }); + if ([view isKindOfClass:portalViewClass]) { + return true; } else { + return false; + } +} + +UIView * _Nullable getPortalViewSourceView(UIView * _Nonnull portalView) { + if (!isViewPortalView(portalView)) { return nil; } + UIView *view = (UIView *)portalView; + return view.sourceView; } @protocol GraphicsFilterProtocol @@ -214,3 +231,48 @@ - (NSObject * _Nullable)filterWithName:(NSString * _Nonnull)name; NSObject * _Nullable makeLuminanceToAlphaFilter() { return [(id)NSClassFromString(@"CAFilter") filterWithName:@"luminanceToAlpha"]; } + +void setLayerDisableScreenshots(CALayer * _Nonnull layer, bool disableScreenshots) { + static UITextField *textField = nil; + static UIView *secureView = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + textField = [[UITextField alloc] init]; + for (UIView *subview in textField.subviews) { + if ([NSStringFromClass([subview class]) containsString:@"TextLayoutCanvasView"]) { + secureView = subview; + break; + } + } + }); + if (secureView == nil) { + return; + } + + CALayer *previousLayer = secureView.layer; + [secureView setValue:layer forKey:@"layer"]; + if (disableScreenshots) { + textField.secureTextEntry = false; + textField.secureTextEntry = true; + } else { + textField.secureTextEntry = true; + textField.secureTextEntry = false; + } + [secureView setValue:previousLayer forKey:@"layer"]; +} + +void setLayerContentsMaskMode(CALayer * _Nonnull layer, bool maskMode) { + static NSString *key = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + key = [@"contents" stringByAppendingString:@"Swizzle"]; + }); + if (key == nil) { + return; + } + if (maskMode) { + [layer setValue:@"AAAA" forKey:key]; + } else { + [layer setValue:@"RGBA" forKey:key]; + } +} diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.h b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.h index 46e724a905f..5afbb217100 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.h +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.h @@ -55,3 +55,5 @@ void applyKeyboardAutocorrection(UITextView * _Nonnull textView); - (void)fixScrollDisplayLink; @end + +void snapshotViewByDrawingInContext(UIView * _Nonnull view); diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m index c4f8086c23f..6e3f53029d0 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m @@ -483,3 +483,7 @@ + (void)initialize { } @end + +void snapshotViewByDrawingInContext(UIView * _Nonnull view) { + [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:false]; +} diff --git a/submodules/UndoUI/BUILD b/submodules/UndoUI/BUILD index 311179b5050..369f0e7bcf0 100644 --- a/submodules/UndoUI/BUILD +++ b/submodules/UndoUI/BUILD @@ -28,6 +28,7 @@ swift_library( "//submodules/AccountContext:AccountContext", "//submodules/ComponentFlow:ComponentFlow", "//submodules/AnimatedAvatarSetNode:AnimatedAvatarSetNode", + "//submodules/TelegramUI/Components/EmojiStatusComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/UndoUI/Sources/UndoOverlayController.swift b/submodules/UndoUI/Sources/UndoOverlayController.swift index a5775b10087..26dfa5c535a 100644 --- a/submodules/UndoUI/Sources/UndoOverlayController.swift +++ b/submodules/UndoUI/Sources/UndoOverlayController.swift @@ -35,6 +35,7 @@ public enum UndoOverlayContent { case voiceChatFlag(text: String) case voiceChatCanSpeak(text: String) case sticker(context: AccountContext, file: TelegramMediaFile, loop: Bool, title: String?, text: String, undoText: String?, customAction: (() -> Void)?) + case customEmoji(context: AccountContext, file: TelegramMediaFile, loop: Bool, title: String?, text: String, undoText: String?, customAction: (() -> Void)?) case copy(text: String) case mediaSaved(text: String) case paymentSent(currencyValue: String, itemTitle: String) diff --git a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift index 592379be071..17a66b81285 100644 --- a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift +++ b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift @@ -17,6 +17,8 @@ import StickerResources import AvatarNode import AccountContext import AnimatedAvatarSetNode +import ComponentFlow +import EmojiStatusComponent final class UndoOverlayControllerNode: ViewControllerTracingNode { private let presentationData: PresentationData @@ -36,6 +38,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { private var stillStickerNode: TransformImageNode? private var stickerImageSize: CGSize? private var stickerOffset: CGPoint? + private var emojiStatus: ComponentView? private let titleNode: ImmediateTextNode private let textNode: ImmediateTextNode private let buttonNode: HighlightTrackingButtonNode @@ -392,7 +395,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white) let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .natural) self.textNode.attributedText = attributedText - self.textNode.maximumNumberOfLines = 2 + self.textNode.maximumNumberOfLines = 10 displayUndo = false self.originalRemainingSeconds = 5 case let .swipeToReply(title, text): @@ -780,7 +783,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { return ("URL", contents) }), textAlignment: .natural) self.textNode.attributedText = attributedText - self.textNode.maximumNumberOfLines = 2 + self.textNode.maximumNumberOfLines = 5 if text.contains("](") { isUserInteractionEnabled = true @@ -812,6 +815,65 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: resource._asResource(), isVideo: file.isVideoSticker), width: 80, height: 80, playbackMode: loop ? .loop : .once, mode: .cached) } } + case let .customEmoji(context, file, loop, title, text, customUndoText, _): + self.avatarNode = nil + self.iconNode = nil + self.iconCheckNode = nil + self.animationNode = nil + + let imageBoundingSize = CGSize(width: 34.0, height: 34.0) + + let emojiStatus = ComponentView() + self.emojiStatus = emojiStatus + let _ = emojiStatus.update( + transition: .immediate, + component: AnyComponent(EmojiStatusComponent( + context: context, + animationCache: context.animationCache, + animationRenderer: context.animationRenderer, + content: .animation( + content: .file(file: file), + size: imageBoundingSize, + placeholderColor: UIColor(white: 1.0, alpha: 0.1), + themeColor: .white, + loopMode: loop ? .forever : .count(1) + ), + isVisibleForAnimations: true, + useSharedAnimation: false, + action: nil + )), + environment: {}, + containerSize: imageBoundingSize + ) + + self.stickerImageSize = imageBoundingSize + + if let title = title { + self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white) + } else { + self.titleNode.attributedText = nil + } + + let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white) + let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white) + let link = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: undoTextColor) + let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { contents in + return ("URL", contents) + }), textAlignment: .natural) + self.textNode.attributedText = attributedText + self.textNode.maximumNumberOfLines = 5 + + if text.contains("](") { + isUserInteractionEnabled = true + } + + if let customUndoText = customUndoText { + undoText = customUndoText + displayUndo = true + } else { + displayUndo = false + } + self.originalRemainingSeconds = isUserInteractionEnabled ? 5 : 3 case let .copy(text): self.avatarNode = nil self.iconNode = nil @@ -901,7 +963,6 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { self.avatarNode = nil self.iconNode = nil self.iconCheckNode = nil - self.animationNode = AnimationNode(animation: animation, colors: colors, scale: scale) self.animatedStickerNode = nil @@ -945,12 +1006,9 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { } case let .premiumPaywall(title, text, customUndoText, timeout, linkAction): self.avatarNode = nil - self.iconNode = ASImageNode() - self.iconNode?.displayWithoutProcessing = true - self.iconNode?.displaysAsynchronously = false - self.iconNode?.image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/PremiumIcon"), color: .white) + self.iconNode = nil self.iconCheckNode = nil - self.animationNode = nil + self.animationNode = AnimationNode(animation: "PremiumStar", colors: [:], scale: 0.066) self.animatedStickerNode = nil if let title = title, text.isEmpty { @@ -1013,7 +1071,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { self.iconNode?.contentMode = .scaleAspectFill self.iconNode?.image = image self.iconNode?.cornerRadius = round ? 16.0 : 4.0 - self.iconImageSize = CGSize(width: 32.0, height: 32.0) + self.iconImageSize = image.size.aspectFitted(CGSize(width: 128.0, height: 32.0)) self.iconCheckNode = nil self.animationNode = nil self.animatedStickerNode = nil @@ -1097,8 +1155,8 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { self.panelNode.backgroundColor = .clear } self.panelNode.clipsToBounds = true - self.panelNode.cornerRadius = 9.0 - + self.panelNode.cornerRadius = 14.0 + self.panelWrapperNode = ASDisplayNode() self.effectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark)) @@ -1114,7 +1172,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { } else { self.isUserInteractionEnabled = false } - case .sticker: + case .sticker, .customEmoji: self.isUserInteractionEnabled = displayUndo case .dice: self.panelWrapperNode.clipsToBounds = true @@ -1144,6 +1202,9 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { self.iconCheckNode.flatMap(self.panelWrapperNode.addSubnode) self.animationNode.flatMap(self.panelWrapperNode.addSubnode) self.stillStickerNode.flatMap(self.panelWrapperNode.addSubnode) + if let emojiStatusView = self.emojiStatus?.view { + self.panelWrapperNode.view.addSubview(emojiStatusView) + } self.animatedStickerNode.flatMap(self.panelWrapperNode.addSubnode) self.slotMachineNode.flatMap(self.panelWrapperNode.addSubnode) self.avatarNode.flatMap(self.panelWrapperNode.addSubnode) @@ -1184,6 +1245,10 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { override func didLoad() { super.didLoad() + if #available(iOS 13.0, *) { + self.panelNode.layer.cornerCurve = .continuous + } + self.panelNode.view.addSubview(self.effectView) } @@ -1201,6 +1266,12 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { } else { let _ = self.action(.undo) } + case let .customEmoji(_, _, _, _, _, _, customAction): + if let customAction = customAction { + customAction() + } else { + let _ = self.action(.undo) + } default: let _ = self.action(.undo) } @@ -1310,7 +1381,9 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { } var leftInset: CGFloat = 50.0 - if let iconSize = preferredSize { + if let iconImageSize = self.iconImageSize { + leftInset = 9.0 + iconImageSize.width + 9.0 + } else if let iconSize = preferredSize { if iconSize.width > leftInset { leftInset = iconSize.width - 8.0 } @@ -1397,7 +1470,12 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { iconSize = CGSize() } - let iconFrame = CGRect(origin: CGPoint(x: floor((leftInset - iconSize.width) / 2.0), y: floor((contentHeight - iconSize.height) / 2.0) + verticalOffset), size: iconSize) + let iconFrame: CGRect + if self.iconImageSize != nil { + iconFrame = CGRect(origin: CGPoint(x: 9.0, y: floor((contentHeight - iconSize.height) / 2.0) + verticalOffset), size: iconSize) + } else { + iconFrame = CGRect(origin: CGPoint(x: floor((leftInset - iconSize.width) / 2.0), y: floor((contentHeight - iconSize.height) / 2.0) + verticalOffset), size: iconSize) + } transition.updateFrame(node: iconNode, frame: iconFrame) if let iconCheckNode = self.iconCheckNode { @@ -1430,6 +1508,10 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { transition.updateFrame(node: stillStickerNode, frame: iconFrame) } + if let emojiStatusView = self.emojiStatus?.view { + transition.updateFrame(view: emojiStatusView, frame: iconFrame) + } + if let animatedStickerNode = self.animatedStickerNode { animatedStickerNode.updateLayout(size: iconFrame.size) transition.updateFrame(node: animatedStickerNode, frame: iconFrame) diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index c6660b3b370..385210f53d4 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -620,7 +620,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) return .single(.result(.peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil)))) } } else { - return .single(.result(.peer(nil, .info))) + return .single(.result(.peer(nil, .info(nil)))) } } case let .peer(reference, parameter): @@ -780,7 +780,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) return .single(.result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil)))) } } else { - return .single(.result(.peer(nil, .info))) + return .single(.result(.peer(nil, .info(nil)))) } } case let .peerId(peerId): @@ -796,9 +796,9 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) return .single(.progress) |> then(context.engine.peers.importContactToken(token: token) |> mapToSignal { peer -> Signal in if let peer = peer { - return .single(.result(.peer(peer._asPeer(), .info))) + return .single(.result(.peer(peer._asPeer(), .info(nil)))) } else { - return .single(.result(.peer(nil, .info))) + return .single(.result(.peer(nil, .info(nil)))) } }) case let .privateMessage(messageId, threadId, timecode): diff --git a/submodules/Utils/LokiRng/BUILD b/submodules/Utils/LokiRng/BUILD new file mode 100644 index 00000000000..ac5a34a442c --- /dev/null +++ b/submodules/Utils/LokiRng/BUILD @@ -0,0 +1,24 @@ + +objc_library( + name = "LokiRng", + enable_modules = True, + module_name = "LokiRng", + srcs = glob([ + "Sources/**/*.m", + "Sources/**/*.mm", + "Sources/**/*.h", + "Sources/**/*.cpp", + ]), + hdrs = glob([ + "PublicHeaders/**/*.h", + ]), + includes = [ + "PublicHeaders", + ], + sdk_frameworks = [ + "Foundation", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/Utils/LokiRng/Package.swift b/submodules/Utils/LokiRng/Package.swift new file mode 100644 index 00000000000..f2c2bcf28a1 --- /dev/null +++ b/submodules/Utils/LokiRng/Package.swift @@ -0,0 +1,27 @@ +// swift-tools-version:5.5 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "LokiRng", + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "LokiRng", + targets: ["LokiRng"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "LokiRng", + dependencies: [], + path: ".", + publicHeadersPath: "PublicHeaders"), + ] +) diff --git a/submodules/Utils/LokiRng/PublicHeaders/LokiRng/LokiRng.h b/submodules/Utils/LokiRng/PublicHeaders/LokiRng/LokiRng.h new file mode 100644 index 00000000000..e1c0abb7c30 --- /dev/null +++ b/submodules/Utils/LokiRng/PublicHeaders/LokiRng/LokiRng.h @@ -0,0 +1,14 @@ +#ifndef LokiRng_h +#define LokiRng_h + +#import + +@interface LokiRng : NSObject + +- (instancetype _Nonnull)initWithSeed0:(NSUInteger)seed0 seed1:(NSUInteger)seed1 seed2:(NSUInteger)seed2; + +- (float)next; + +@end + +#endif /* LokiRng_h */ diff --git a/submodules/Utils/LokiRng/Sources/LokiRng.mm b/submodules/Utils/LokiRng/Sources/LokiRng.mm new file mode 100644 index 00000000000..3f8240d3612 --- /dev/null +++ b/submodules/Utils/LokiRng/Sources/LokiRng.mm @@ -0,0 +1,64 @@ +#import + +static uint32_t tausStep(const uint32_t z, const int32_t s1, const int32_t s2, const int32_t s3, const uint32_t M) { + uint32_t b = (((z << s1) ^ z) >> s2); + return (((z & M) << s3) ^ b); +} + +@interface LokiRng () { + float _seed; +} + +@end + +@implementation LokiRng + +- (instancetype _Nonnull)initWithSeed0:(NSUInteger)seed0 seed1:(NSUInteger)seed1 seed2:(NSUInteger)seed2 { + self = [super init]; + if (self != nil) { + uint32_t seed = ((uint32_t)seed0) * 1099087573U; + uint32_t seedb = ((uint32_t)seed1) * 1099087573U; + uint32_t seedc = ((uint32_t)seed2) * 1099087573U; + + // Round 1: Randomise seed + uint32_t z1 = tausStep(seed,13,19,12,429496729U); + uint32_t z2 = tausStep(seed,2,25,4,4294967288U); + uint32_t z3 = tausStep(seed,3,11,17,429496280U); + uint32_t z4 = (1664525*seed + 1013904223U); + + // Round 2: Randomise seed again using second seed + uint32_t r1 = (z1^z2^z3^z4^seedb); + + z1 = tausStep(r1,13,19,12,429496729U); + z2 = tausStep(r1,2,25,4,4294967288U); + z3 = tausStep(r1,3,11,17,429496280U); + z4 = (1664525*r1 + 1013904223U); + + // Round 3: Randomise seed again using third seed + r1 = (z1^z2^z3^z4^seedc); + + z1 = tausStep(r1,13,19,12,429496729U); + z2 = tausStep(r1,2,25,4,4294967288U); + z3 = tausStep(r1,3,11,17,429496280U); + z4 = (1664525*r1 + 1013904223U); + + _seed = (z1^z2^z3^z4) * 2.3283064365387e-10f; + } + return self; +} + +- (float)next { + uint32_t hashed_seed = _seed * 1099087573U; + + uint32_t z1 = tausStep(hashed_seed,13,19,12,429496729U); + uint32_t z2 = tausStep(hashed_seed,2,25,4,4294967288U); + uint32_t z3 = tausStep(hashed_seed,3,11,17,429496280U); + uint32_t z4 = (1664525*hashed_seed + 1013904223U); + + float old_seed = _seed; + _seed = (z1^z2^z3^z4) * 2.3283064365387e-10f; + + return old_seed; +} + +@end diff --git a/submodules/Utils/ShelfPack/BUILD b/submodules/Utils/ShelfPack/BUILD new file mode 100644 index 00000000000..bade2d97bda --- /dev/null +++ b/submodules/Utils/ShelfPack/BUILD @@ -0,0 +1,24 @@ + +objc_library( + name = "ShelfPack", + enable_modules = True, + module_name = "ShelfPack", + srcs = glob([ + "Sources/**/*.m", + "Sources/**/*.mm", + "Sources/**/*.h", + "Sources/**/*.cpp", + ]), + hdrs = glob([ + "PublicHeaders/**/*.h", + ]), + includes = [ + "PublicHeaders", + ], + sdk_frameworks = [ + "Foundation", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/Utils/ShelfPack/PublicHeaders/ShelfPack/ShelfPack.h b/submodules/Utils/ShelfPack/PublicHeaders/ShelfPack/ShelfPack.h new file mode 100644 index 00000000000..f91e84b34b9 --- /dev/null +++ b/submodules/Utils/ShelfPack/PublicHeaders/ShelfPack/ShelfPack.h @@ -0,0 +1,33 @@ +#ifndef ShelfPack_h +#define ShelfPack_h + +#import + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int32_t itemId; + int32_t x; + int32_t y; + int32_t width; + int32_t height; +} ShelfPackItem; + +@interface ShelfPackContext : NSObject + +@property (nonatomic, readonly) bool isEmpty; + +- (instancetype _Nonnull)initWithWidth:(int32_t)width height:(int32_t)height; + +- (ShelfPackItem)addItemWithWidth:(int32_t)width height:(int32_t)height; +- (void)removeItem:(int32_t)itemId; + +@end + +#ifdef __cplusplus +} +#endif + +#endif /* ShelfPack_h */ diff --git a/submodules/Utils/ShelfPack/Sources/ShelfPack.mm b/submodules/Utils/ShelfPack/Sources/ShelfPack.mm new file mode 100644 index 00000000000..741cdb36fde --- /dev/null +++ b/submodules/Utils/ShelfPack/Sources/ShelfPack.mm @@ -0,0 +1,58 @@ +#import + +#import "shelf-pack.hpp" +#import + +@interface ShelfPackContext () { + std::unique_ptr _pack; + int32_t _nextItemId; + int _count; +} + +@end + +@implementation ShelfPackContext + +- (instancetype _Nonnull)initWithWidth:(int32_t)width height:(int32_t)height { + self = [super init]; + if (self != nil) { + _pack = std::make_unique(width, height); + } + return self; +} + +- (bool)isEmpty { + return _count == 0; +} + +- (ShelfPackItem)addItemWithWidth:(int32_t)width height:(int32_t)height { + ShelfPackItem item = { + .itemId = -1, + .x = 0, + .y = 0, + .width = 0, + .height = 0 + }; + + int32_t itemId = _nextItemId; + _nextItemId += 1; + if (const auto bin = _pack->packOne(itemId, width, height)) { + item.itemId = bin->id; + item.x = bin->x; + item.y = bin->y; + item.width = bin->w; + item.height = bin->h; + _count += 1; + } + + return item; +} + +- (void)removeItem:(int32_t)itemId { + if (const auto bin = _pack->getBin(itemId)) { + _pack->unref(*bin); + _count -= 1; + } +} + +@end diff --git a/submodules/Utils/ShelfPack/Sources/shelf-pack.hpp b/submodules/Utils/ShelfPack/Sources/shelf-pack.hpp new file mode 100644 index 00000000000..3a78bfe05c1 --- /dev/null +++ b/submodules/Utils/ShelfPack/Sources/shelf-pack.hpp @@ -0,0 +1,535 @@ +#ifndef SHELF_PACK_HPP +#define SHELF_PACK_HPP + +#include +#include +#include +#include +#include +#include + +namespace mapbox { + +const char * const SHELF_PACK_VERSION = "2.1.1"; + + + +class Bin { + friend class ShelfPack; + +public: + /** + * Create a new Bin. + * + * @class Bin + * @param {int32_t} id Unique bin identifier + * @param {int32_t} [w1=-1] Width of the new Bin + * @param {int32_t} [h1=-1] Height of the new Bin + * @param {int32_t} [maxw1=-1] Maximum Width of the new Bin + * @param {int32_t} [maxh1=-1] Maximum Height of the new Bin + * @param {int32_t} [x1=-1] X location of the Bin + * @param {int32_t} [y1=-1] Y location of the Bin + * + * @example + * Bin b(-1, 12, 16); + */ + explicit Bin( + int32_t id1 = -1, + int32_t w1 = -1, + int32_t h1 = -1, + int32_t maxw1 = -1, + int32_t maxh1 = -1, + int32_t x1 = -1, + int32_t y1 = -1 + ) : id(id1), w(w1), h(h1), maxw(maxw1), maxh(maxh1), x(x1), y(y1), refcount_(0) { + + if (maxw == -1) { + maxw = w; + } + if (maxh == -1) { + maxh = h; + } + } + + int32_t id; + int32_t w; + int32_t h; + int32_t maxw; + int32_t maxh; + int32_t x; + int32_t y; + + int32_t refcount() const { return refcount_; } + +private: + + int32_t refcount_; +}; + + +class Shelf { +public: + /** + * Create a new Shelf. + * + * @class Shelf + * @param {int32_t} y1 Top coordinate of the new shelf + * @param {int32_t} w1 Width of the new shelf + * @param {int32_t} h1 Height of the new shelf + * + * @example + * Shelf shelf(64, 512, 24); + */ + explicit Shelf(int32_t y1, int32_t w1, int32_t h1) : + x_(0), y_(y1), w_(w1), h_(h1), wfree_(w1) { } + + + /** + * Allocate a single bin into the shelf. + * Bin is stored in a `bins_` container. + * Returned pointer is stable until the shelf is destroyed. + * + * @param {int32_t} id Unique bin identifier, pass -1 to generate a new one + * @param {int32_t} w1 Width of the bin to allocate + * @param {int32_t} h1 Height of the bin to allocate + * @returns {Bin*} `Bin` pointer with `id`, `x`, `y`, `w`, `h` members + * + * @example + * Bin* result = shelf.alloc(-1, 12, 16); + */ + Bin* alloc(int32_t id, int32_t w1, int32_t h1) { + if (w1 > wfree_ || h1 > h_) { + return nullptr; + } + int32_t x1 = x_; + x_ += w1; + wfree_ -= w1; + bins_.emplace_back(id, w1, h1, w1, h_, x1, y_); + return &bins_.back(); + } + + + /** + * Resize the shelf. + * + * @param {int32_t} w1 Requested new width of the shelf + * @returns {bool} `true` if resize succeeded, `false` if failed + * + * @example + * shelf.resize(512); + */ + bool resize(int32_t w1) { + wfree_ += (w1 - w_); + w_ = w1; + return true; + } + + int32_t x() const { return x_; } + int32_t y() const { return y_; } + int32_t w() const { return w_; } + int32_t h() const { return h_; } + int32_t wfree() const { return wfree_; } + +private: + int32_t x_; + int32_t y_; + int32_t w_; + int32_t h_; + int32_t wfree_; + + std::deque bins_; +}; + + + +class ShelfPack { +public: + + struct ShelfPackOptions { + inline ShelfPackOptions() : autoResize(false) { }; + bool autoResize; + }; + + struct PackOptions { + inline PackOptions() : inPlace(false) { }; + bool inPlace; + }; + + + /** + * Create a new ShelfPack bin allocator. + * + * Uses the Shelf Best Height Fit algorithm from + * http://clb.demon.fi/files/RectangleBinPack.pdf + * + * @class ShelfPack + * @param {int32_t} [w=64] Initial width of the sprite + * @param {int32_t} [h=64] Initial width of the sprite + * @param {ShelfPackOptions} [options] + * @param {bool} [options.autoResize=false] If `true`, the sprite will automatically grow + * + * @example + * ShelfPack::ShelfPackOptions options; + * options.autoResize = false; + * ShelfPack sprite = new ShelfPack(64, 64, options); + */ + explicit ShelfPack(int32_t w = 0, int32_t h = 0, const ShelfPackOptions &options = ShelfPackOptions{}) { + width_ = w > 0 ? w : 64; + height_ = h > 0 ? h : 64; + autoResize_ = options.autoResize; + maxId_ = 0; + } + + + /** + * Batch pack multiple bins into the sprite. + * + * @param {vector} bins Array of requested bins - each object should have `w`, `h` values + * @param {PackOptions} [options] + * @param {bool} [options.inPlace=false] If `true`, the supplied bin objects will be updated inplace with `x` and `y` values + * @returns {vector} Array of Bin pointers - each bin is a struct with `x`, `y`, `w`, `h` values + * + * @example + * std::vector moreBins; + * moreBins.emplace_back(-1, 12, 24); + * moreBins.emplace_back(-1, 12, 12); + * moreBins.emplace_back(-1, 10, 10); + * + * ShelfPack::PackOptions options; + * options.inPlace = true; + * std::vector results = sprite.pack(moreBins, options); + */ + std::vector pack(std::vector &bins, const PackOptions &options = PackOptions{}) { + std::vector results; + + for (auto& bin : bins) { + if (bin.w > 0 && bin.h > 0) { + Bin* allocation = packOne(bin.id, bin.w, bin.h); + if (!allocation) { + continue; + } + if (options.inPlace) { + bin.id = allocation->id; + bin.x = allocation->x; + bin.y = allocation->y; + } + results.push_back(allocation); + } + } + + shrink(); + + return results; + } + + + /** + * Pack a single bin into the sprite. + * + * @param {int32_t} id Unique bin identifier, pass -1 to generate a new one + * @param {int32_t} w Width of the bin to allocate + * @param {int32_t} h Height of the bin to allocate + * @returns {Bin*} Pointer to a packed Bin with `id`, `x`, `y`, `w`, `h` members + * + * @example + * Bin* result = sprite.packOne(-1, 12, 16); + */ + Bin* packOne(int32_t id, int32_t w, int32_t h) { + int32_t y = 0; + int32_t waste = 0; + struct { + Shelf* pshelf = nullptr; + Bin* pfreebin = nullptr; + int32_t waste = std::numeric_limits::max(); + } best; + + // if id was supplied, attempt a lookup.. + if (id != -1) { + Bin* pbin = getBin(id); + if (pbin) { // we packed this bin already + ref(*pbin); + return pbin; + } + maxId_ = std::max(id, maxId_); + } else { + id = ++maxId_; + } + + // First try to reuse a free bin.. + for (auto& freebin : freebins_) { + // exactly the right height and width, use it.. + if (h == freebin->maxh && w == freebin->maxw) { + return allocFreebin(freebin, id, w, h); + } + // not enough height or width, skip it.. + if (h > freebin->maxh || w > freebin->maxw) { + continue; + } + // extra height or width, minimize wasted area.. + if (h <= freebin->maxh && w <= freebin->maxw) { + waste = (freebin->maxw * freebin->maxh) - (w * h); + if (waste < best.waste) { + best.waste = waste; + best.pfreebin = freebin; + } + } + } + + // Next find the best shelf + for (auto& shelf : shelves_) { + y += shelf.h(); + + // not enough width on this shelf, skip it.. + if (w > shelf.wfree()) { + continue; + } + // exactly the right height, pack it.. + if (h == shelf.h()) { + return allocShelf(shelf, id, w, h); + } + // not enough height, skip it.. + if (h > shelf.h()) { + continue; + } + // extra height, minimize wasted area.. + if (h < shelf.h()) { + waste = (shelf.h() - h) * w; + if (waste < best.waste) { + best.waste = waste; + best.pshelf = &shelf; + } + } + } + + if (best.pfreebin) { + return allocFreebin(best.pfreebin, id, w, h); + } + + if (best.pshelf) { + return allocShelf(*best.pshelf, id, w, h); + } + + // No free bins or shelves.. add shelf.. + if (h <= (height_ - y) && w <= width_) { + shelves_.emplace_back(y, width_, h); + return allocShelf(shelves_.back(), id, w, h); + } + + // No room for more shelves.. + // If `autoResize` option is set, grow the sprite as follows: + // * double whichever sprite dimension is smaller (`w1` or `h1`) + // * if sprite dimensions are equal, grow width before height + // * accomodate very large bin requests (big `w` or `h`) + if (autoResize_) { + int32_t h1, h2, w1, w2; + + h1 = h2 = height_; + w1 = w2 = width_; + + if (w1 <= h1 || w > w1) { // grow width.. + w2 = std::max(w, w1) * 2; + } + if (h1 < w1 || h > h1) { // grow height.. + h2 = std::max(h, h1) * 2; + } + + resize(w2, h2); + return packOne(id, w, h); // retry + } + + return nullptr; + } + + + /** + * + * Shrink the width/height of the sprite to the bare minimum. + * Since shelf-pack doubles first width, then height when running out of shelf space + * this can result in fairly large unused space both in width and height if that happens + * towards the end of bin packing. + */ + void shrink() { + if (shelves_.size()) { + int32_t w2 = 0; + int32_t h2 = 0; + + for (auto& shelf : shelves_) { + h2 += shelf.h(); + w2 = std::max(shelf.w() - shelf.wfree(), w2); + } + + resize(w2, h2); + } + } + + + /** + * Return a packed bin given its id, or nullptr if the id is not found + * + * @param {int32_t} id Unique identifier for this bin, + * @returns {Bin*} Pointer to a packed Bin with `id`, `x`, `y`, `w`, `h` members + * + * @example + * Bin* result = sprite.getBin(5); + */ + Bin* getBin(int32_t id) { + std::map::iterator it = usedbins_.find(id); + return (it == usedbins_.end()) ? nullptr : it->second; + } + + + /** + * Increment the ref count of a bin and update statistics. + * + * @param {Bin&} bin Bin reference + * @returns {int32_t} New refcount of the bin + * + * @example + * Bin* bin = sprite.getBin(5); + * if (bin) { + * sprite.ref(*bin); + * } + */ + int32_t ref(Bin& bin) { + if (++bin.refcount_ == 1) { // a new Bin.. record height in stats historgram.. + int32_t h = bin.h; + stats_[h] = (stats_[h] | 0) + 1; + } + + return bin.refcount_; + }; + + + /** + * Decrement the ref count of a bin and update statistics. + * The bin will be automatically marked as free space once the refcount reaches 0. + * Memory for the bin is not freed, as unreferenced bins may be reused later. + * + * @param {Bin&} bin Bin reference + * @returns {int32_t} New refcount of the bin + * + * @example + * Bin* bin = sprite.getBin(5); + * if (bin) { + * sprite.unref(*bin); + * } + */ + int32_t unref(Bin& bin) { + if (bin.refcount_ == 0) { + return 0; + } + + if (--bin.refcount_ == 0) { + stats_[bin.h]--; + usedbins_.erase(bin.id); + freebins_.push_back(&bin); + } + + return bin.refcount_; + } + + + /** + * Clear the sprite and reset statistics. + * + * @example + * sprite.clear(); + */ + void clear() { + shelves_.clear(); + freebins_.clear(); + usedbins_.clear(); + stats_.clear(); + maxId_ = 0; + } + + + /** + * Resize the sprite. + * + * @param {int32_t} w Requested new sprite width + * @param {int32_t} h Requested new sprite height + * @returns {bool} `true` if resize succeeded, `false` if failed + * + * @example + * sprite.resize(256, 256); + */ + bool resize(int32_t w, int32_t h) { + width_ = w; + height_ = h; + for (auto& shelf : shelves_) { + shelf.resize(width_); + } + return true; + } + + int32_t width() const { return width_; } + int32_t height() const { return height_; } + + +private: + + /** + * Called by packOne() to allocate a bin by reusing an existing freebin + * + * @private + * @param {Bin*} bin Pointer to a freebin to reuse + * @param {int32_t} w Width of the bin to allocate + * @param {int32_t} h Height of the bin to allocate + * @param {int32_t} id Unique identifier for this bin + * @returns {Bin*} Pointer to a Bin with `id`, `x`, `y`, `w`, `h` properties + * + * @example + * Bin* bin = sprite.allocFreebin(pfreebin, 12, 16, 5); + */ + Bin* allocFreebin(Bin* bin, int32_t id, int32_t w, int32_t h) { + freebins_.erase(std::remove(freebins_.begin(), freebins_.end(), bin), freebins_.end()); + bin->id = id; + bin->w = w; + bin->h = h; + bin->refcount_ = 0; + usedbins_[id] = bin; + ref(*bin); + return bin; + } + + + /** + * Called by `packOne() to allocate bin on an existing shelf + * Memory for the bin is allocated on the heap by `shelf.alloc()` + * + * @private + * @param {Shelf&} shelf Reference to the shelf to allocate the bin on + * @param {int32_t} w Width of the bin to allocate + * @param {int32_t} h Height of the bin to allocate + * @param {int32_t} id Unique identifier for this bin + * @returns {Bin*} Pointer to a Bin with `id`, `x`, `y`, `w`, `h` properties + * + * @example + * Bin* bin = sprite.allocShelf(shelf, 12, 16, 5); + */ + Bin* allocShelf(Shelf& shelf, int32_t id, int32_t w, int32_t h) { + Bin* pbin = shelf.alloc(id, w, h); + if (pbin) { + usedbins_[id] = pbin; + ref(*pbin); + } + return pbin; + } + + + int32_t width_; + int32_t height_; + int32_t maxId_; + bool autoResize_; + + std::deque shelves_; + std::map usedbins_; + std::vector freebins_; + std::map stats_; +}; + + +} // namespace mapbox + +#endif diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift index dc5c7d58d8f..f4de1aa7756 100644 --- a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift @@ -82,7 +82,7 @@ public protocol WallpaperBackgroundNode: ASDisplayNode { var isReady: Signal { get } var rotation: CGFloat { get set } - func update(wallpaper: TelegramWallpaper) + func update(wallpaper: TelegramWallpaper, animated: Bool) func _internalUpdateIsSettingUpWallpaper() func updateLayout(size: CGSize, displayMode: WallpaperDisplayMode, transition: ContainedViewLayoutTransition) func updateIsLooping(_ isLooping: Bool) @@ -747,8 +747,6 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode private let dimLayer: SimpleLayer private var isGeneratingPatternImage: Bool = false - private let bakedBackgroundView: UIImageView - private var validLayout: (CGSize, WallpaperDisplayMode)? private var wallpaper: TelegramWallpaper? private var isSettingUpWallpaper: Bool = false @@ -861,9 +859,6 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode self.patternImageLayer = EffectImageLayer() - self.bakedBackgroundView = UIImageView() - self.bakedBackgroundView.isHidden = true - self.dimLayer = SimpleLayer() self.dimLayer.opacity = 0.0 self.dimLayer.backgroundColor = UIColor.black.cgColor @@ -925,12 +920,22 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode self.dimLayer.opacity = dimAlpha } - func update(wallpaper: TelegramWallpaper) { + func update(wallpaper: TelegramWallpaper, animated: Bool) { if self.wallpaper == wallpaper { return } + let previousWallpaper = self.wallpaper self.wallpaper = wallpaper - + + if let _ = previousWallpaper, animated { + if let snapshotView = self.view.snapshotView(afterScreenUpdates: false) { + self.view.addSubview(snapshotView) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in + snapshotView.removeFromSuperview() + }) + } + } + var gradientColors: [UInt32] = [] var gradientAngle: Int32 = 0 diff --git a/submodules/WebUI/Sources/WebAppWebView.swift b/submodules/WebUI/Sources/WebAppWebView.swift index 34fa385c296..b3262d12621 100644 --- a/submodules/WebUI/Sources/WebAppWebView.swift +++ b/submodules/WebUI/Sources/WebAppWebView.swift @@ -35,39 +35,85 @@ private class WebViewTouchGestureRecognizer: UITapGestureRecognizer { } } +private let eventProxySource = "var TelegramWebviewProxyProto = function() {}; " + + "TelegramWebviewProxyProto.prototype.postEvent = function(eventName, eventData) { " + + "window.webkit.messageHandlers.performAction.postMessage({'eventName': eventName, 'eventData': eventData}); " + + "}; " + +"var TelegramWebviewProxy = new TelegramWebviewProxyProto();" + +private let selectionSource = "var css = '*{-webkit-touch-callout:none;} :not(input):not(textarea):not([\"contenteditable\"=\"true\"]){-webkit-user-select:none;}';" + + " var head = document.head || document.getElementsByTagName('head')[0];" + + " var style = document.createElement('style'); style.type = 'text/css';" + + " style.appendChild(document.createTextNode(css)); head.appendChild(style);" + +private let videoSource = """ +function disableWebkitEnterFullscreen(videoElement) { + if (videoElement && videoElement.webkitEnterFullscreen) { + Object.defineProperty(videoElement, 'webkitEnterFullscreen', { + value: undefined + }); + } +} + +function disableFullscreenOnExistingVideos() { + document.querySelectorAll('video').forEach(disableWebkitEnterFullscreen); +} + +function handleMutations(mutations) { + mutations.forEach((mutation) => { + if (mutation.addedNodes && mutation.addedNodes.length > 0) { + mutation.addedNodes.forEach((newNode) => { + if (newNode.tagName === 'VIDEO') { + disableWebkitEnterFullscreen(newNode); + } + if (newNode.querySelectorAll) { + newNode.querySelectorAll('video').forEach(disableWebkitEnterFullscreen); + } + }); + } + }); +} + +disableFullscreenOnExistingVideos(); + +const observer = new MutationObserver(handleMutations); + +observer.observe(document.body, { + childList: true, + subtree: true +}); + +function disconnectObserver() { + observer.disconnect(); +} +""" + final class WebAppWebView: WKWebView { var handleScriptMessage: (WKScriptMessage) -> Void = { _ in } init() { let configuration = WKWebViewConfiguration() - let userController = WKUserContentController() - - let js = "var TelegramWebviewProxyProto = function() {}; " + - "TelegramWebviewProxyProto.prototype.postEvent = function(eventName, eventData) { " + - "window.webkit.messageHandlers.performAction.postMessage({'eventName': eventName, 'eventData': eventData}); " + - "}; " + - "var TelegramWebviewProxy = new TelegramWebviewProxyProto();" - + let contentController = WKUserContentController() + var handleScriptMessageImpl: ((WKScriptMessage) -> Void)? - let userScript = WKUserScript(source: js, injectionTime: .atDocumentStart, forMainFrameOnly: false) - userController.addUserScript(userScript) - userController.add(WeakGameScriptMessageHandler { message in + let eventProxyScript = WKUserScript(source: eventProxySource, injectionTime: .atDocumentStart, forMainFrameOnly: false) + contentController.addUserScript(eventProxyScript) + contentController.add(WeakGameScriptMessageHandler { message in handleScriptMessageImpl?(message) }, name: "performAction") - let selectionString = "var css = '*{-webkit-touch-callout:none;} :not(input):not(textarea):not([\"contenteditable\"=\"true\"]){-webkit-user-select:none;}';" - + " var head = document.head || document.getElementsByTagName('head')[0];" - + " var style = document.createElement('style'); style.type = 'text/css';" + - " style.appendChild(document.createTextNode(css)); head.appendChild(style);" - let selectionScript: WKUserScript = WKUserScript(source: selectionString, injectionTime: .atDocumentEnd, forMainFrameOnly: true) - userController.addUserScript(selectionScript) + let selectionScript = WKUserScript(source: selectionSource, injectionTime: .atDocumentEnd, forMainFrameOnly: true) + contentController.addUserScript(selectionScript) + + let videoScript = WKUserScript(source: videoSource, injectionTime: .atDocumentStart, forMainFrameOnly: false) + contentController.addUserScript(videoScript) - configuration.userContentController = userController + configuration.userContentController = contentController configuration.allowsInlineMediaPlayback = true configuration.allowsPictureInPictureMediaPlayback = false if #available(iOS 10.0, *) { - configuration.mediaTypesRequiringUserActionForPlayback = .all + configuration.mediaTypesRequiringUserActionForPlayback = .audio } else { configuration.mediaPlaybackRequiresUserAction = true } diff --git a/submodules/lottie-ios/Sources/Public/Animation/AnimationView.swift b/submodules/lottie-ios/Sources/Public/Animation/AnimationView.swift index cc33f87ed8c..c5a8656e86a 100644 --- a/submodules/lottie-ios/Sources/Public/Animation/AnimationView.swift +++ b/submodules/lottie-ios/Sources/Public/Animation/AnimationView.swift @@ -402,7 +402,7 @@ final public class AnimationView: AnimationViewBase { if self.needsWorkaroundDisplayLink != oldValue { if self.needsWorkaroundDisplayLink { if self.workaroundDisplayLink == nil { - self.workaroundDisplayLink = SharedDisplayLinkDriver.shared.add { [weak self] in + self.workaroundDisplayLink = SharedDisplayLinkDriver.shared.add { [weak self] _ in let _ = self?.realtimeAnimationProgress } } diff --git a/swift_deps.bzl b/swift_deps.bzl index 60827c08d44..f85c834671a 100644 --- a/swift_deps.bzl +++ b/swift_deps.bzl @@ -36,7 +36,7 @@ def swift_dependencies(): # branch: develop swift_package( name = "swiftpkg_nicegram_assistant_ios", - commit = "c3686ba5c4f4ad5eb0273bc9dfd5784e0971a5a2", + commit = "35b2c2c88c8d2cb3d25cfa95e15e588f90578994", dependencies_index = "@//:swift_deps_index.json", remote = "git@bitbucket.org:mobyrix/nicegram-assistant-ios.git", ) @@ -52,7 +52,7 @@ def swift_dependencies(): # version: 5.15.5 swift_package( name = "swiftpkg_sdwebimage", - commit = "1b9a2e902cbde5fdf362faa0f4fd76ea74d74305", + commit = "0383fd49fe4d9ae43f150f24693550ebe6ef0d14", dependencies_index = "@//:swift_deps_index.json", remote = "https://github.com/SDWebImage/SDWebImage.git", ) @@ -76,7 +76,7 @@ def swift_dependencies(): # version: 1.2.3 swift_package( name = "swiftpkg_swift_argument_parser", - commit = "8f4d2753f0e4778c76d5f05ad16c74f707390531", + commit = "c8ed701b513cf5177118a175d85fbbbcd707ab41", dependencies_index = "@//:swift_deps_index.json", remote = "https://github.com/apple/swift-argument-parser", ) diff --git a/swift_deps_index.json b/swift_deps_index.json index 6196b008b80..c0997b5221f 100644 --- a/swift_deps_index.json +++ b/swift_deps_index.json @@ -798,7 +798,7 @@ "name": "swiftpkg_nicegram_assistant_ios", "identity": "nicegram-assistant-ios", "remote": { - "commit": "c3686ba5c4f4ad5eb0273bc9dfd5784e0971a5a2", + "commit": "35b2c2c88c8d2cb3d25cfa95e15e588f90578994", "remote": "git@bitbucket.org:mobyrix/nicegram-assistant-ios.git", "branch": "develop" } @@ -816,9 +816,9 @@ "name": "swiftpkg_sdwebimage", "identity": "sdwebimage", "remote": { - "commit": "1b9a2e902cbde5fdf362faa0f4fd76ea74d74305", + "commit": "0383fd49fe4d9ae43f150f24693550ebe6ef0d14", "remote": "https://github.com/SDWebImage/SDWebImage.git", - "version": "5.18.5" + "version": "5.18.6" } }, { @@ -843,9 +843,9 @@ "name": "swiftpkg_swift_argument_parser", "identity": "swift-argument-parser", "remote": { - "commit": "8f4d2753f0e4778c76d5f05ad16c74f707390531", + "commit": "c8ed701b513cf5177118a175d85fbbbcd707ab41", "remote": "https://github.com/apple/swift-argument-parser", - "version": "1.2.3" + "version": "1.3.0" } }, { diff --git a/versions.json b/versions.json index aa5c3d33a2c..597ea8a72fb 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "app": "1.4.7", + "app": "1.4.8", "bazel": "6.4.0", "xcode": "15.0.1" }